ECMAScript 6的let
应该提供块范围而不会引起头痛。有人可以解释为什么函数中的i
下面的代码解析为循环中的最后一个值(就像使用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在let
循环中使用for
,这应该将变量绑定在循环体的范围内。当我在块中使用临时变量时,事情就像我期望的那样。为什么这有必要?
"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
测试了脚本。
答案 0 :(得分:49)
for(let i;;){}
i
为循环的每次迭代获取一个新绑定。
这意味着每个闭包捕获不同的i
实例。所以012
的结果是截至目前的正确结果。在Chrome v47 +中运行此功能后,您将获得正确的结果。当您在IE11和Edge中运行它时,目前似乎产生了错误的结果(333
)。
有关此错误/功能的更多信息,请参阅this page;
中的链接因为使用let
表达式时,每次迭代都会创建一个链接到前一个范围的新词法范围。这对使用let
表达式具有性能影响,该表达式报告为here。
答案 1 :(得分:13)
我通过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
是每次迭代的唯一变量。最终将有三个单独的_i
实例。
每个回调函数都可以看到其对应的_i
,甚至可以根据需要进行操作,与其他范围中的_i
无关。
(您可以通过在回调中执行_i
来确认有三个不同的console.log(i++)
。在先前的回调中更改_i
不会影响以后回调的输出。)< / p>
在每次迭代结束时,_i
的值将复制到i
。因此,在迭代期间更改唯一内部变量 将影响外部迭代变量。
很高兴看到ES6延续了WTFJS的悠久传统。
答案 2 :(得分:1)
恕我直言 - 首先实施此LET(产生初始版本的结果)的程序员在理智方面做得正确;在实施过程中,他们可能没有看过规范。
使用单个变量更有意义,但是作用于for循环。特别是因为人们可以根据循环中的条件随意更改该变量。
但等等 - 你可以更改循环变量。 WTFJS!但是,如果您尝试在内部范围内更改它,它现在将无法正常工作因为它是一个新变量。
我不喜欢我必须做的事情来获得我想要的东西(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变量的意图是什么......或者如果SANITY统治了宇宙,那就是这样。
所以你的第一个例子现在可用于FF;它产生0,1,2。你可以解决这个问题。我把问题称为WTFJS。
PS。我对WTFJS的提及来自上面的JoeyTwiddle;这听起来像我今天之前应该知道的模因,但今天是学习它的好时机。