在let
出现之前,for
循环定义的迭代变量会渗透到循环体外部:
for(var i = 0;i<5;i++){
// 执行逻辑
}
console.log(i); //5
改成使用let
之后,这个问题就消失了,因为迭代变量的额作用域仅限于for
循环内部:
for(let i= 0;i<5;i++){
// 执行逻辑
}
console.log(i); //i is not defined
在使用var的时候,最常见的问题就是对迭代变量的奇特声明和修改:
for(var i = 0;i<5;i++){
setTimeout(() => {
console.log(i); // 5 5 5 5 5
}, 0);
}
//你可能以为它会打印0 1 2 3 4
//实际上会输出 5 5 5 5 5
为了能够更加直观的看出执行的过程,将代码改造成一下过程:
for(var i = 0;i<5;i++){
console.time("计时器一");
setTimeout(() => {
console.time("计时器二");
console.log(i);
console.timeEnd("计时器二");
}, 0);
console.timeEnd("计时器一");
}
可以直观的看到,计时器一执行完了才去执行计时器二,在执行计时器二的时候,i
已经是5了。
之所以会这样是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时逻辑时,所有的i
都是同一个变量,因为输出的都是同一个最终值。
而在使用let
声明迭代变量时,JavaScript
引擎在后台会为每个迭代循环声明一个新的迭代变量。而每个setTimeout
引用的都是不同的变量实例,所以输出的时我们期望的值,也就是循环过程中每个迭代变量的值:
for(let i = 0;i<5;i++){
setTimeout(() => {
console.log(i); //0 1 2 3 4
}, 0);
}
for(let i = 0;i<5;i++){
console.time("计时器一");
setTimeout(() => {
console.time("计时器二");
console.log(i);
console.timeEnd("计时器二");
}, 0);
console.timeEnd("计时器一");
}
这种每次迭代声明一个独立变量实例的行为适用于所有风格的for
循环,包括for in
和for of
小建议:
尽量少或者不使用var
去声明,使用let
有助于提升代码质量,因为变量有了明确的作用域、声明位置。
文章大多表述文字源于:红宝书第四版28-29页,少部分表述为个人,表达不到位请原谅