做了一些研究后我放弃了这个。我的问题如下:
来自post ,我理解下面的代码。
var funcs = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
var item = "item" + i; // inside
console.log("item: " + item + ", i: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
Result:
item: item3, i: 3
item: item3, i: 3
item: item3, i: 3
但是如果我在 function() 之外更改移动 var item = "item"+ i
,结果将会不同。这是为什么?
var funcs = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
var item = "item" + i; // outside
funcs[i] = function() { // and store them in funcs
console.log("item: " + item + ", i: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
Result:
item: item2, i: 3
item: item2, i: 3
item: item2, i: 3
最佳答案
您面临的关键问题是闭包通过引用捕获变量,而不是通过值。例如:
var i = 0;
var f = function(){ i++; console.log(i); }
var g = function(){ i++; console.log(i); }
f(); g(); f(); g();
这将显示 1, 2, 3, 4,因为 f
和 g
没有捕获变量 i
的值,而是捕获变量本身,因此都改变同一个变量。
一个非常常见的错误是在循环中创建多个闭包,忘记所有这些闭包都会捕获同一个变量(循环中使用的变量),因此当此循环变量递增时,所有闭包都会被捕获。已经创建的闭包将会看到它的变化。例如在您的代码中
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
var item = "item" + i; // inside
console.log("item: " + item + ", i: " + i);
};
}
item
是函数的本地变量,但 i
来自外部,并且在所有函数之间共享。例如,这可以通过
i = 42;
funcs[0](); funcs[1](); funcs[2](); // Displays 42 three times
i = 99;
funcs[0](); funcs[1](); funcs[2](); // Now they all display 99
在你问题的第二种情况下,情况略有不同,因为
for (var i = 0; i < 3; i++) {
var item = "item" + i;
funcs[i] = function() {
console.log("item: " + item + ", i: " + i);
};
}
此外,item
变量现在也是共享的,因为它是在函数外部定义的,并且闭包捕获它。在显示屏中,您可以看到分配给它的最后一个值。
要记住的关键一点是,在 Javascript 中,变量是函数的本地变量,而不是 {...}
block 的本地变量(例如在 C++ 中) ),因此区分因素是变量是在函数内部还是在函数外部声明,而不是它们是在循环内部还是外部声明。
为了克服这个常见问题,并为循环中创建的每个闭包建立单独的索引,您通常会看到类似的代码
var f = (function(i){return function(){ ... };})(i);
其中创建一个函数并立即调用它以返回另一个函数。
原因是创建单独的新变量的唯一方法是函数,这给我们留下了创建返回函数的函数的模式:这里有用的闭包(内部) case 将捕获每次迭代都不同的变量(因为它是外部的参数)。
将代码更改为
for (var i = 0; i < 3; i++) {
funcs[i] = (function(i){return function() {
var item = "item" + i;
console.log("item: " + item + ", i: " + i);
};})(i);
}
将按预期工作(即每个函数现在都有自己的 i
值)。
编辑
在当前的 Javascript 中,上述所有内容都适用于 var
变量(函数作用域),但您也可以使用 let
block 作用域变量。
这意味着现在可以简化代码,例如:
lambdas = [];
for (let i=0; i<10; i++) {
let j = i*i; // Variable j is block scoped
lambdas.push(function(){ return j; });
}
console.log(lambdas[3]()); // ==> output 9
console.log(lambdas[5]()); // ==> output 25
(使用 var j = i*i;
相反,两者都会得到 81)
关于带有 for 循环的 Javascript 闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26687859/