一般说明
当您在 for 循环结构中使用 let 时,会为每次调用循环创建一个新变量 i,其范围仅限于循环块(在环形)。
循环的第一次迭代从for 循环初始化程序(在您的示例中为i = 1)获取其值。每次循环迭代创建的其他新 i 变量从循环的先前调用的 i 获取它们的值,而不是从 i = 1 获取它们的值,这就是为什么它们没有全部初始化为 1。
因此,每次循环都会有一个新变量 i 与所有其他变量分开,并且每个新变量都使用前一个变量的值进行初始化,然后由 @ 中的 i++ 处理987654334@ 循环声明。
对于你的 ES6 代码:
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
}
在 ES5 中,这将是一个本质上等价的结构。如果你真的研究了这个,它可以为你上面的 ES6 代码中实际发生的事情提供非常丰富的信息:
(function() {
for (var i = 1; i <= 5; i++) {
i = (function(j) {
setTimeout(function(){
console.log(j);
},100);
return j;
})(i);
}
})();
需要两个 IIFE(立即调用的函数表达式)来模拟这一点。外层隔离var i,使其不会泄漏出for 循环,而内层则为for 循环的每次调用提供单独的范围和单独的变量。 return j 和 i = (function(j) {...})(i) 是为了展示循环的下一次迭代如何受到循环变量修改的影响。
希望这能说明 let 对于 ES6 中的 for 循环是多么有用,以及当您需要/想要此功能时它替换了多少其他代码。
现在解决您的具体问题
为此,我已经阅读了互联网上的解释,例如 - 让为每个循环创建一个变量声明,这是块级声明。所以基本上它在 { }
内创建了一个范围
let 定义了具有块作用域的变量(不是像var 这样的函数作用域)。而且,描绘for 循环的{ 和} 确实定义了一个范围。
此外,变量 i 在循环块之外被声明、初始化和递增,即 for 循环语句中的花括号。
嗯,不完全是。 for 循环是关于如何初始化为第一次调用循环创建的第一个 i 的指令。 let i = 1 出现在循环之外的事实确实看起来有点令人困惑,但这实际上只是一个指令,说明它何时为循环的第一次调用创建第一个 i 变量。第一个 i 变量实际上并不存在于循环范围之外。
那么,在每次迭代中,不是同一个变量 i 都被递增和利用了吗?
没有。当 ES6 遇到带有 let 定义的 for 循环时,它会为循环的每次迭代创建一个新变量。
let 究竟如何为每个循环创建一个变量声明并具有前一次迭代的值?
这不是let 做的。这是 ES6+ JS 解释器中的 for 循环逻辑。这是for 循环的特殊行为,该循环具有用let 声明的索引初始化程序。所以,这是let和for的组合行为,但真正的逻辑是解释器执行for循环的方式。
修改循环变量时的特殊情况
let 在for 循环中还有一个特殊情况。如果您在循环中分配i 的值,它将更改i 的特定值,并且还将影响下一次迭代的i 值。这有点特殊,但它仍然允许您在循环中操作i 的值。
for(let i = 1; i <= 5; i++) {
let j = i;
setTimeout(function(){
console.log(j);
},100);
if (i === 2) {
i++; // bump the loop increment to skip the 3 value
}
}
这将创建输出:
1
2
4
5
因此,这将跳过循环的 3 迭代,因为当 i === 2 时,我们将其递增到 3,然后 for 循环执行其 i++ 迭代并将其提升到 4,有效地跳过3 迭代。