循环和闭包。对于和变量
Loops and closures. For and Var
我找到了很多解释这个问题的主题,关于如何使用 var 修复以下代码,比如这个
http://conceptf1.blogspot.com/2013/11/javascript-closures.html or this one JavaScript closure inside loops – simple practical example.
但是我真的不明白为什么用var不行,用let不行。
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
// outputs 3 3 3
我真的不知道...
因为 var
是函数范围的(即具有周围函数的范围),而 let
和 const
是块范围的 - 因此有自己的值在每个块内(无论是 if
- 或 else
-块还是循环的每次迭代,如您的情况)。块作用域变量不在块外定义。函数范围的变量一直保留到函数结束。
您的 i
变量是函数范围的,这意味着一旦您的循环完成,它仍然存在于第一个循环之外并且具有 3
的值。因此,一旦调用数组函数,它就会从上层作用域(即 3
)获取 i
并将其输出。例如,如果您使用 let
,您将获得每次迭代的新绑定,并且您的 i
的值将保留初始声明值。
编辑:
所以为了输出顺序值,需要把var
换成let
:
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
// now `i` is bound to the scope & keeps its initial value
console.log("My value: " + i);
};
}
与 let
不同,var
被提升到循环范围之外。事实上,您的 i
变量将始终等于最后一次迭代(在您的示例中为 3)。 let
没有这个问题,因为它没有被吊起来。
ES6's let
是块范围,这意味着它像许多其他传统语言一样在 {}
内有自己的范围。但相比之下 var
在你的代码中是一个全局变量。
在第一个for
循环中,function
被赋值给func[i]
3次,最终值为3,但没有执行。如果您先执行 loop
中的函数,您将获得预期的输出,例如:
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
funcs[i](); // execution of func
}
所以,重要的是你的函数在哪个上下文中执行。
现在,当 funcs[j]()
在您的代码中第一次执行时,i
的值已经是 3
。如果你想记录增加的值,你必须像下面这样将其作为参数传递:
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function(j) { // and store them in funcs
console.log("My value: " + j); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](j); // and now let's run each one to see
}
我找到了很多解释这个问题的主题,关于如何使用 var 修复以下代码,比如这个 http://conceptf1.blogspot.com/2013/11/javascript-closures.html or this one JavaScript closure inside loops – simple practical example.
但是我真的不明白为什么用var不行,用let不行。
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
// outputs 3 3 3
我真的不知道...
因为 var
是函数范围的(即具有周围函数的范围),而 let
和 const
是块范围的 - 因此有自己的值在每个块内(无论是 if
- 或 else
-块还是循环的每次迭代,如您的情况)。块作用域变量不在块外定义。函数范围的变量一直保留到函数结束。
您的 i
变量是函数范围的,这意味着一旦您的循环完成,它仍然存在于第一个循环之外并且具有 3
的值。因此,一旦调用数组函数,它就会从上层作用域(即 3
)获取 i
并将其输出。例如,如果您使用 let
,您将获得每次迭代的新绑定,并且您的 i
的值将保留初始声明值。
编辑:
所以为了输出顺序值,需要把var
换成let
:
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
// now `i` is bound to the scope & keeps its initial value
console.log("My value: " + i);
};
}
与 let
不同,var
被提升到循环范围之外。事实上,您的 i
变量将始终等于最后一次迭代(在您的示例中为 3)。 let
没有这个问题,因为它没有被吊起来。
ES6's let
是块范围,这意味着它像许多其他传统语言一样在 {}
内有自己的范围。但相比之下 var
在你的代码中是一个全局变量。
在第一个for
循环中,function
被赋值给func[i]
3次,最终值为3,但没有执行。如果您先执行 loop
中的函数,您将获得预期的输出,例如:
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
funcs[i](); // execution of func
}
所以,重要的是你的函数在哪个上下文中执行。
现在,当 funcs[j]()
在您的代码中第一次执行时,i
的值已经是 3
。如果你想记录增加的值,你必须像下面这样将其作为参数传递:
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function(j) { // and store them in funcs
console.log("My value: " + j); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](j); // and now let's run each one to see
}