使用块范围变量创建JS闭包时会发生什么?

时间:2014-06-06 09:17:08

标签: javascript closures language-lawyer

不久前,I asked a question使用此示例JS代码......

for (var myindex = 0; myindex < mylist.length; myindex += 1) {
    var copyindex = myindex;
    MyAsync(mylist[myindex], function () { alert(copyindex); });
}

(答案主要是调用命名的函数,它将循环中的每个变量分开。)

我今天的问题是,上面的示例代码中实际发生了什么?每个警报调用都传递相同的copyindex变量,即使每次循环遍历时都会声明一个新的警报调用。

JS语言是否需要这种行为? (通过声明带有块范围的变量,我真的将它声明为函数的顶部。)

或者,这可能是我测试的少数浏览器的实现细节,未来的JS平台没有义务将 copyindex 的许多实例链接在一起吗?

1 个答案:

答案 0 :(得分:2)

  

每个警报调用都会传递相同的copyindex变量,即使每次都声明了一个新的...

这是一个常见的误解。 var没有块范围,因此您的代码真的就是:

var copyindex;
for (var myindex = 0; myindex < mylist.length; myindex += 1) {
    copyindex = myindex;
    MyAsync(mylist[myindex], function () { alert(copyindex); });
}

更多(在我的博客上):Poor, misunderstood var,当然,在规范中 - 它已在§10.5 - Declaration Binding Instantiation中介绍。


ES6将通过let关键字引入块范围变量。您使用的地方let关键字非常重要。

让我们以var

开头的一个更简单的例子
for (var x = 0; x < 3; ++x) {
    setTimeout(function() { console.log("x = " + x); }, 0);
}
console.log("typeof x = " + typeof x);

我们知道会给我们

number
3
3
3

...因为var被提升到范围的顶部,因此我们创建的所有三个函数都使用相同的x,并且x存在于循环之外。 (当然,我们首先看到typeof结果,因为其他结果发生在最小超时之后。)

如果我们在同一个地方使用let

for (let x = 0; x < 3; ++x) {
    setTimeout(function() { console.log("x = " + x); }, 0);
}
console.log("typeof x = " + typeof x);

我们得到了

undefined
3
3
3

...因为x的范围是循环,而不是循环迭代。这三个函数仍使用相同的x,唯一的区别是循环外不存在x

但是如果我们在主体中使用let

for (let n = 0; n < 3; ++n) {
    let x = n;
    setTimeout(function() { console.log("x = " + x); }, 0);
}
console.log("typeof x = " + typeof x);

我们得到了

undefined
0
1
2

...因为现在x的范围是每次迭代的主体,而不是整个循环。

您可以使用NodeJS自行尝试,只需确保为其提供--harmony标志即可启用ES6功能。