55

ECMAScript 6let应该提供块范围而不会让人头疼。有人可以解释为什么在下面的代码i中函数解析为循环中的最后一个值(就像 with 一样var)而不是当前迭代中的值?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    things["fun" + i] = function() {
        console.log(i);
    };
}

things["fun0"](); // prints 3
things["fun1"](); // prints 3
things["fun2"](); // prints 3

根据在循环中使用的MDN,应该将变量绑定在循环体的范围内。当我在块内使用临时变量时,事情就像我期望的那样工作。为什么这是必要的?letfor

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    let index = i;
    things["fun" + i] = function() {
        console.log(index);
    };
}

things["fun0"](); // prints 0
things["fun1"](); // prints 1
things["fun2"](); // prints 2

我用 Traceur 和node --harmony.

4

3 回答 3

66

squint 的答案不再是最新的。在ECMA 6 规范中,指定的行为是

for(let i;;){}

i为循环的每次迭代获取一个新的绑定。

这意味着每个闭包都会捕获一个不同的i实例。所以结果012是目前的正确结果。当你在 Chrome v47+ 中运行它时,你会得到正确的结果。当您在 IE11 和 Edge 中运行它时,目前333似乎产生了不正确的结果 ( )。

有关此错误/功能的更多信息可以在此页面中的链接中找到;

因为当使用let表达式时,每次迭代都会创建一个新的词法范围,链接到前一个范围。let这对使用表达式有性能影响,此处报告。

于 2016-03-05T09:25:02.593 回答
19

我通过Babel传递了这段代码,因此我们可以根据熟悉的 ES5 来理解行为:

for (let i = 0; i < 3; i++) {
    i++;
    things["fun" + i] = function() {
        console.log(i);
    };
    i--;
}

以下是转译为 ES5 的代码:

var _loop = function _loop(_i) {
    _i++;
    things["fun" + _i] = function () {
        console.log(_i);
    };
    _i--;
    i = _i;
};

for (var i = 0; i < 3; i++) {
    _loop(i);
}

我们可以看到使用了两个变量。

  • 在外部范围内i是随着我们迭代而变化的变量。

  • 在内部范围内_i是每次迭代的唯一变量。最终将有 3 个单独的实例_i

    每个回调函数都可以看到其对应_i的 ,甚至可以根据需要对其进行操作,而与_i其他范围内的 s 无关。

    _i(您可以通过在回调内部进行操作来确认存在三个不同的 s console.log(i++)。更改_i较早的回调不会影响以后回调的输出。)

在每次迭代结束时,将 的值_i复制到i. 因此,在迭代过程中改变唯一的内部变量将影响外部迭代变量。

很高兴看到 ES6 延续了 WTFJS 的悠久传统。

于 2017-06-05T10:41:18.480 回答
4

恕我直言——首先实现这个 LET(产生你的初始版本的结果)的程序员在理智方面做得正确;在实施过程中,他们可能没有看过规范。

使用单个变量更有意义,但范围仅限于 for 循环。特别是因为人们应该可以根据循环中的条件随意更改该变量。

但是等等——你可以改变循环变量。哇!但是,如果您尝试在内部范围内更改它,它现在将不起作用,因为它是一个新变量。

我不喜欢我必须做的事情来获得我想要的东西(for 本地的单个变量):

{
    let x = 0;
    for (; x < length; x++)
    {
        things["fun" + x] = function() {
            console.log(x);
        };
    }
}

在哪里修改更直观(如果是虚构的)版本以处理每次迭代的新变量:

for (let x = 0; x < length; x++)
{
    let y = x;
    things["fun" + y] = function() {
        console.log(y);
    };
}

很清楚我对 y 变量的意图是什么。或者如果理智统治宇宙的话。

因此,您的第一个示例现在可以在 FF 中使用;它会产生 0、1、2。您可以将问题称为已修复。我把这个问题称为 WTFJS。

附言。我对 WTFJS 的引用来自上面的 JoeyTwiddle;这听起来像是我今天之前应该知道的模因,但今天是学习它的好时机。

于 2018-03-07T22:19:21.543 回答