为什么我不使用“ let”来防止setTimeout和连续变量的经典关闭问题?

时间:2018-12-18 17:44:32

标签: javascript closures

查看此代码:

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 200);
}

这是经典的错误,它反复打印i的最终值,而不打印所需的连续输出。

我知道可以使用let而不是var来解决该问题:

for(let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 200);
}

让我困惑的是为什么这个 不能解决它:

for(var i = 0; i < 5; i++) {
    setTimeout(function() {
        let n = i;
        console.log(n);
    }, 200);
}

现在,出于与先前代码相同的原因,应对其进行修复,但该错误仍然存​​在。为什么会这样?

2 个答案:

答案 0 :(得分:1)

您仍在i回调函数中引用setTimeout,因此仍然有一个闭包。实际上,这与您的第一个带有计时器的“经典”闭合问题的例子没有什么不同。

let示例避免了该问题的原因是,即使在计时器回调中引用了i,它也与早期循环迭代中的i不同。每次迭代都有自己的i

答案 1 :(得分:1)

在for循环的开头,let声明有一种特殊的行为。该变量在每次迭代中都唯一声明。这就是为什么示例2“有效”的原因。

在示例3中,var i声明仅发生一次,并且该值在每次迭代时都会更新。 let也在执行时声明。因此,每次在示例#3中执行回调函数时,第一次是在for循环的所有迭代都发生之后,let n = i被声明(在执行时),此时i是for循环中声明的单个i

我鼓励您快速阅读本快速入门。 https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch5.md#block-scoping-revisited

console.log(a);
var a = 10;

将打印undefined,因为在console.log a时已声明但未分配。

console.log(a);
let (or const) a = 10;

将抛出TypeError,因为在console.log a时尚未声明。