关于闭包:让javascript中的vs var

let vs var in javascript

本问题已经有最佳答案,请猛点这里访问。

我理解let具有块范围,var具有功能范围。但我不明白在这种情况下,如何使用let来解决问题

1
2
3
4
5
6
7
8
9
10
11
12
13
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

这都与变量的范围有关。让我们尝试将这两个部分包装成函数,并观察输出:

1
2
3
4
5
6
7
8
9
10
11
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语句。因此,结果符合预期:

1
2
3
4
5
6
7
8
9
10
11
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();


首先,输出将是四次而不是五次(如您的评论中所述)。我把你的代码贴在babel repl里,这就是我得到的,

1
2
3
4
5
6
7
8
9
10
11
12
13
"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);
}

你看到了让内部如何运作吗?-)


您仍然可以使用var作为setTimeout。可以使用立即调用的函数表达式(IIFE)在setTimeout周围创建一个闭包,以便i的值被setTimeout函数识别。

1
2
3
4
5
6
7
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
(function(i){
setTimeout(function() {
   console.log(arr[i])
}, 1000)})(i);
}