Abh*_*ari 2 javascript scope for-loop let ecmascript-6
请考虑下面的片段-
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,内部的日志setTimeout将包含i每个 for 循环迭代的变量值,即日志如下
1
2
3
4
5
Run Code Online (Sandbox Code Playgroud)
为此,我已经阅读了 Internet 上的解释,例如 -let为每个循环创建一个变量声明,这是块级声明。所以基本上它在{ }.
但我对这个说法有点困惑。如果let为每个循环创建一个变量声明,它不会总是1按照循环初始化语句进行初始化let i=1吗?此外,变量i在循环块外声明、初始化和递增,即for循环语句中的花括号。那么,在每次迭代中,不是i增加和使用相同的变量吗?究竟如何let为每个循环创建一个变量声明并具有前一次迭代的值?
jfr*_*d00 10
一般说明
当你像你展示的那样let在for循环结构中使用时,会为循环的i每次调用创建一个新变量,该变量的范围仅限于循环块(无法在循环外访问)。
循环的第一次迭代从for循环初始值设定项(i = 1在您的示例中)获取其值。i每次循环迭代创建的其他新变量从ifor 上一次循环调用中获取它们的值,而不是从 thei = 1中获取它们的值,这就是它们没有全部初始化为 的原因1。
这样,通过每次循环有一个新的变量i是从所有其他的单独的和每一个新的一个初始化与前一个的值,然后被处理由i++在for循环声明。
对于您的 ES6 代码:
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log(i);
},100);
} Run Code Online (Sandbox Code Playgroud)
在 ES5 中,这将是一个本质上等效的结构。如果你真的研究过这个,它可以为你上面 ES6 代码中实际发生的事情提供非常丰富的信息:
(function() {
for (var i = 1; i <= 5; i++) {
i = (function(j) {
setTimeout(function(){
console.log(j);
},100);
return j;
})(i);
}
})();Run Code Online (Sandbox Code Playgroud)
这需要两个 IIFE(立即调用的函数表达式)来模拟。外层将 隔离var i,使其不会泄漏到for循环之外,而内层则为for循环的每次调用提供单独的作用域和单独的变量。该return j和i = (function(j) {...})(i)是展示如何循环的下一次迭代通过修改循环变量的影响。
希望这说明了ES6 中的letforfor循环非常有用,以及当您需要/想要此功能时它替换了多少其他代码。
现在回答您的具体问题
为此,我已经阅读了 Internet 上的解释,例如 - 让为每个循环创建一个变量声明,这是块级声明。所以基本上它在 { } 内创建了一个范围
let定义具有块作用域的变量(不是像 那样的函数作用域var)。而且,描述循环的{and确实定义了一个范围。}for
此外,变量 i 在循环块外(即 for 循环语句中的花括号)被声明、初始化和递增。
嗯,不完全是。该for回路是如何初始化第一的指令i为循环的第一次调用创建。let i = 1出现在循环之外的事实确实看起来有点令人困惑,但它实际上只是一个指令,说明它在i为循环的第一次调用创建第一个变量时要做什么。第一个i变量实际上并不存在于循环范围之外。
那么,在每次迭代中,i 增加和使用的变量不是同一个吗?
不会。当 ES6 遇到for带有let定义的循环时,它会为循环的每次迭代创建一个新变量。
let 究竟是如何为每个循环创建一个变量声明并具有前一次迭代的值的?
它不是let在做这个。这是forES6+ JS 解释器中的循环逻辑。对于for具有用 声明的索引初始值设定项的循环来说,这是一种特殊行为let。因此,它是letand的组合行为for,但真正的逻辑在于for解释器执行循环的方式。
修改循环变量时的特殊情况
对于letinfor循环还有一个特殊情况。如果您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
}
}Run Code Online (Sandbox Code Playgroud)
这将创建输出:
1
2
4
5
Run Code Online (Sandbox Code Playgroud)
因此,这会跳过3循环的迭代,因为当 时i === 2,我们将其增加到 3,然后for循环进行i++迭代并将其提升到4,从而有效地跳过3迭代。
像这样的 for 循环:
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}Run Code Online (Sandbox Code Playgroud)
和这样做一样:
{
let i = 1;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 2;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 3;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 4;
setTimeout(function() {
console.log(i);
}, 100);
}
{
let i = 5;
setTimeout(function() {
console.log(i);
}, 100);
}Run Code Online (Sandbox Code Playgroud)
该变量在 for 循环的范围内被声明和分配了五次,并且每个实例都与其他实例完全分开。