JavaScript不支持使用局部variables的闭包?

这个代码我很困惑:

var closures = []; function create() { for (var i = 0; i < 5; i++) { closures[i] = function() { alert("i = " + i); }; } } function run() { for (var i = 0; i < 5; i++) { closures[i](); } } create(); run(); 

从我的理解应该打印0,1,2,3,4(这不是封闭的概念?)。

相反,它打印5,5,5,5,5。

我试过Rhino和Firefox。

有人可以向我解释这种行为吗? Thx提前。

通过添加一个匿名函数修复了Jon的答案:

 function create() { for (var i = 0; i < 5; i++) { closures[i] = (function(tmp) { return function() { alert("i = " + tmp); }; })(i); } } 

其解释是JavaScript的作用域是function级的,而不是块级的,创build闭包只是意味着封闭作用域被添加到封装函数的词法环境中。

循环结束后,函数级variablesi的值为5 ,这就是内部函数“看到”的内容。


作为一个方面说明:你应该小心不必要的函数对象创build,尤其是在循环中; 它效率低下,如果涉及DOM对象,创build循环引用很容易,因此在Internet Explorer中引入内存泄漏。

我想这可能是你想要的:

 var closures = []; function createClosure(i) { closures[i] = function() { alert("i = " + i); }; } function create() { for (var i = 0; i < 5; i++) { createClosure(i); } } 

解决的办法是有一个自我执行的lambda包装你的数组推动。 你也把我作为参数传给那个lambda。 自我执行的lambda中的i的值将影响原始i的值,并且所有内容都将按照预期工作:

 function create() { for (var i = 0; i < 5; i++) (function(i) { closures[i] = function() { alert("i = " + i); }; })(i); } 

另一个解决scheme是创build另一个闭包,捕获i的正确值,并将其赋值给另一个在最后的lambda中被“捕获”的variables:

 function create() { for (var i = 0; i < 5; i++) (function() { var x = i; closures.push(function() { alert("i = " + x); }); })(); } 

是closures在这里工作。 每次你循环你正在创build的函数抓住i 。 您创build的每个function都共享相同的i 。 你所看到的问题是,因为他们都分享相同的i他们也分享i的最终价值,因为它是相同的捕获variables。

编辑: Skeet先生的这篇文章解释了一些深度的封闭,特别是以一种更具信息性的方式来解决这个问题。 不过要小心,Javascript和C#处理闭包的方式有一些细微的差别。 跳到“比较捕捉策略:复杂性与权力”一节中,解释这个问题。

John Resig的Learning Advanced JavaScript解释了这一点以及更多。 这是一个交互式的演示文稿,解释了很多有关JavaScript的内容,并且这些示例很有趣,可以阅读和执行。

它有一个关于闭包的章节, 这个例子看起来很像你的。

这是一个破碎的例子:

 var count = 0; for ( var i = 0; i < 4; i++ ) { setTimeout(function(){ assert( i == count++, "Check the value of i." ); }, i * 200); } 

修复:

 var count = 0; for ( var i = 0; i < 4; i++ ) (function(i){ setTimeout(function(){ assert( i == count++, "Check the value of i." ); }, i * 200); })(i); 

只需定义一个内部函数,或将其分配给某个variables:

 closures[i] = function() {... 

不会创build整个执行上下文的私人副本。 直到最近的外部函数正在退出 (此时这些外部variables可能被垃圾回收,所以我们最好抓一个副本),上下文不会被复制。

这就是为什么围绕内部函数包装另一个函数的原因 – 中间人实际执行和退出,提示最内层的函数来保存自己的堆栈副本。

这是你应该做的,以达到你的结果:

 <script> var closures = []; function create() { for (var i = 0; i < 5; i++) { closures[i] = function(number) { alert("i = " + number); }; } } function run() { for (var i = 0; i < 5; i++) { closures[i](i); } } create(); run(); </script>