我对这段代码感到非常困惑:
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提前。
答案 0 :(得分:59)
通过添加额外的匿名函数来修复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中引入内存泄漏。
答案 1 :(得分:9)
我认为这可能是你想要的:
var closures = [];
function createClosure(i) {
closures[i] = function() {
alert("i = " + i);
};
}
function create() {
for (var i = 0; i < 5; i++) {
createClosure(i);
}
}
答案 2 :(得分:9)
解决方案是让一个自动执行的lambda包裹你的数组。你也将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);
});
})();
}
答案 3 :(得分:6)
是封闭在这里工作。每次循环创建的函数时,都会抓取i
。您创建的每个函数共享相同的i
。您看到的问题是,由于它们共享相同的i
,因此它们也共享i
的最终值,因为它是相同的捕获变量。
编辑 This article在某种程度上解释了封闭问题,并特别以一种比我在这里提供更多信息的方式解决了这个问题。 但请小心,因为Javascript和C#处理闭包的方式有一些细微差别。 跳到“比较捕获策略:复杂性与能力”这一部分,对此有所解释问题。
答案 4 :(得分:3)
John Resig的Learning Advanced JavaScript解释了这个以及更多。这是一个交互式演示,解释了很多关于JavaScript的内容,这些示例很有趣,可以阅读和执行。
它有一章关于闭包,而this example看起来很像你的。
这是一个破碎的例子:
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);
答案 5 :(得分:1)
只需定义内部函数,或将其分配给某个变量:
closures[i] = function() {...
不会创建整个执行上下文的私有副本。直到最近的外部函数退出才会复制上下文(此时这些外部变量可能被垃圾收集,因此我们最好抓取副本)。
这就是为什么在你的内部函数周围包含另一个函数的工作原理 - 中间人实际执行和退出,使用最里面的函数来保存他自己的堆栈副本。
答案 6 :(得分:-1)
以下是您应该采取的措施:
<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>