我知道let has block scope和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
首先,输出将是四次而不是五次(如评论中所述)。我将你的代码粘贴在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);
}
你看到现在内部如何运作? :-)
这都与变量的范围有关。让我们尝试将两个部分包装成函数,并观察输出:
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();
所以在第一种情况下,i
将被悬挂,并且由于setTimeout
的异步性质,i
将立即变为4,因为循环结束而无需等待。这将使arr[i]
指向数组中的undefined
元素。
在第二种情况下,i
没有被提升,并且具有对循环的每次迭代的范围访问,使i
准确地可用于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();
你仍然可以使用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);
}