This question already has an answer here:
这个问题在这里已有答案:
- Why let and var bindings behave differently using setTimeout function? [duplicate] 2 answers
- 为什么让set和var绑定使用setTimeout函数表现不同? [重复] 2个答案
I understand that let has block scope and var has functional scope. But I do not understand in this case, how using let will solve the problem
我明白让我们有块范围和var有功能范围。但我不明白在这种情况下,如何使用let将解决问题
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
const arr = [1,2,3,4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
3 个解决方案
#1
1
First of all, the output will be four times and not five times(as mentioned in your comment). I pasted your code in Babel REPL and this is what I got,
首先,输出将是四次而不是五次(如评论中所述)。我把你的代码粘贴在Babel REPL中,这就是我得到的,
"use strict";
var arr = [1, 2, 3, 4];
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
Do you see how let works internally now? :-)
你看到现在内部如何工作吗? :-)
#2
3
This is all related to the scope of the variable. Let's try to wrap both the pieces into functions, and observe the output:
这都与变量的范围有关。让我们尝试将两个部分包装成函数,并观察输出:
function test() {
// `i` will be declared here, making it a non-for-loop scoped variable
const arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
}
test();
So in the first case, i
will be hoisted, and because of the asynchronous nature of setTimeout
, i
will immediately become 4 as the loop ends without waiting. This will make arr[i]
to point to an undefined
element in the array.
所以在第一种情况下,我将被悬挂,并且由于setTimeout的异步性质,我将立即变为4,因为循环结束而不等待。这将使arr [i]指向数组中的未定义元素。
In the second case, i
is not hoisted, and has scoped access to each iteration of the loop, making i
accurately available to console.log
statement. Thus the results are as per the expectations:
在第二种情况下,我没有被提升,并且已经对循环的每次迭代进行了范围访问,使得我可以准确地使用console.log语句。因此结果符合预期:
function test() {
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
}
test();
#3
0
You can still use var
for setTimeout
. You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout
such that the value of i
is recognised by the setTimeout
function.
您仍然可以将var用于setTimeout。您可以使用立即调用的函数表达式(IIFE)在setTimeout周围创建一个闭包,使得i的值由setTimeout函数识别。
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
(function(i){
setTimeout(function() {
console.log(arr[i])
}, 1000)})(i);
}
#1
1
First of all, the output will be four times and not five times(as mentioned in your comment). I pasted your code in Babel REPL and this is what I got,
首先,输出将是四次而不是五次(如评论中所述)。我把你的代码粘贴在Babel REPL中,这就是我得到的,
"use strict";
var arr = [1, 2, 3, 4];
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
Do you see how let works internally now? :-)
你看到现在内部如何工作吗? :-)
#2
3
This is all related to the scope of the variable. Let's try to wrap both the pieces into functions, and observe the output:
这都与变量的范围有关。让我们尝试将两个部分包装成函数,并观察输出:
function test() {
// `i` will be declared here, making it a non-for-loop scoped variable
const arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
}
test();
So in the first case, i
will be hoisted, and because of the asynchronous nature of setTimeout
, i
will immediately become 4 as the loop ends without waiting. This will make arr[i]
to point to an undefined
element in the array.
所以在第一种情况下,我将被悬挂,并且由于setTimeout的异步性质,我将立即变为4,因为循环结束而不等待。这将使arr [i]指向数组中的未定义元素。
In the second case, i
is not hoisted, and has scoped access to each iteration of the loop, making i
accurately available to console.log
statement. Thus the results are as per the expectations:
在第二种情况下,我没有被提升,并且已经对循环的每次迭代进行了范围访问,使得我可以准确地使用console.log语句。因此结果符合预期:
function test() {
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
}
test();
#3
0
You can still use var
for setTimeout
. You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout
such that the value of i
is recognised by the setTimeout
function.
您仍然可以将var用于setTimeout。您可以使用立即调用的函数表达式(IIFE)在setTimeout周围创建一个闭包,使得i的值由setTimeout函数识别。
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
(function(i){
setTimeout(function() {
console.log(arr[i])
}, 1000)})(i);
}