我对这段代码感到非常困惑:
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的作用域是函数级的,而不是块级的,并且创建闭包只意味着封闭的作用域被添加到封闭函数的词法环境中.
循环终止后,函数级变量i
具有值5
,这就是内部函数"看到"的内容.
作为旁注:你应该注意不必要的函数对象创建,特别是在循环中; 这是低效的,如果涉及DOM对象,很容易创建循环引用,从而在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包装你的数组push.你也将i作为参数传递给那个lambda.自执行lambda中的i值将影响原始i的值,一切都将按预期工作:
function create() { for (var i = 0; i < 5; i++) (function(i) { closures[i] = function() { alert("i = " + i); }; })(i); }
另一种解决方案是创建另一个闭包,它捕获i的正确值并将其分配给另一个在最终lambda中"被捕获"的变量:
function create() { for (var i = 0; i < 5; i++) (function() { var x = i; closures.push(function() { alert("i = " + x); }); })(); }
是封闭在这里工作.每次循环你正在创建的函数抓取i
.您创建的每个功能都是相同的i
.您看到的问题是,由于它们都共享相同,i
因此它们也共享最终值,i
因为它是相同的捕获变量.
编辑: Skeet先生的这篇文章在某种程度上解释了封闭问题,并特别以一种比我在这里提供更多信息的方式解决了这个问题. 但是要小心,因为Javascript和C#处理闭包的方式有一些细微的差别. 跳到"比较捕获策略:复杂性与能力"这一部分,以解释这个问题.