无法理解 JS 中这个 for 循环的输出

Hri*_*rma 9 javascript for-loop var let settimeout

我已经明白为什么这段代码的输出应该是3 3 3.

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}
Run Code Online (Sandbox Code Playgroud)

但我无法理解为什么这段代码的输出是0 1 2.

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}
Run Code Online (Sandbox Code Playgroud)

我希望第二个循环的输出更加清晰for。

Arn*_*rat 18

和的范围存在差异,这导致它们的行为不同。letvar

以下是Stack Overflow 上其他答案var中关于和之间差异的引用let。

主要区别在于范围规则。由关键字声明的变量的作用域var为直接函数体(因此为函数作用域),而let变量的作用域为由 表示的直接封闭块{ }(因此为块作用域)。

所以,总而言之,var就是指同一个变量地址。但是,正如let阻塞范围(根据上面的引用)一样,setTimeout()make 中的每个回调都会i具有与前一个不同的值。


一个可能的实验是让let行为像var. 要使let行为类似于var,您可以使用(并运行)下面的代码。

要了解let行为如何var,请继续阅读有关var提升的内容!

let i;
for (i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}
Run Code Online (Sandbox Code Playgroud)

当我们移动let到主作用域时,它的行为就像var(在运行var时位于文件顶部,值为undefined)。

这使得let变量的行为类似于var,从而使循环的输出与变量的输出for相同。letvar

感谢Nick Vu的实验!


下面是吊装的快速概述var。

实际代码(如下所示):

web = "stackoverflow.com";
var web;
Run Code Online (Sandbox Code Playgroud)

理解为:

var web;
web = "stackoverflow.com";
Run Code Online (Sandbox Code Playgroud)

基本上,在任何地方定义变量var都会导致它位于文件的顶部,从而导致循环出现奇怪的行为for。

这就是为什么很多人let更喜欢var; 因为var吊装是多么令人困惑!


let除非绝对需要,否则始终使用var!

  • 您的回答非常有建设性,但有趣的是,您复制了我答案的一部分,但没有引用和提及:)。嗯,我知道这是互联网问题,但有时你需要尊重其他人的作品 (6认同)
  • “let” 情况中令人困惑的部分是“i++”。这意味着每次迭代只有一个“i”,而不是一个“i”。看起来 `i++` 实际上必须意味着 `next_instance_of_i = 1 + previous_instance_of_i` (3认同)

Hri*_*rma 6

for(var i=0 ;i <3 ; i++){
     setTimeOut(()=>console.log(i),1);
}
Run Code Online (Sandbox Code Playgroud)

Js引擎将按以下步骤检查第一个for循环:

  1. 我将被吊起,看起来像这样:
    var i;
    for(i = 0 ;i < 3 ; i++){
       setTimeOut(()=>console.log(i),1);
    }
Run Code Online (Sandbox Code Playgroud)
  1. 循环结束后,它将运行 setTimeOut 3 次,这将打印i的值,现在是 3
{ 
   i = 3 
   setTimeOut(()=>console.log(i,1);
}

{ 
   i = 3
   setTimeOut(()=>console.log(i,1);
}

{ 
   i = 3
   setTimeOut(()=>console.log(i,1);
}
Run Code Online (Sandbox Code Playgroud)
for(let i=0 ;i <3 ; i++){
     setTimeOut(()=>console.log(i),1);
}
Run Code Online (Sandbox Code Playgroud)

Js 引擎将检查第二个 for 循环,如下所示:

  1. 我将被吊起,看起来像这样:
    for(let i=0 ;i < 3 ; i++){
       setTimeOut(()=>console.log(i),1);
    }
Run Code Online (Sandbox Code Playgroud)

对于第一次迭代, i 的值将为 0 ,对于第二次迭代, i 的值将为 1 ,对于第三次迭代, i 的值将为 2 。

想象一下,它是三个不同的范围块,如下所示:

{ 
   i = 0 
   setTimeOut(()=>console.log(i,1);
}

{ 
   i = 1 
   setTimeOut(()=>console.log(i,1);
}

{ 
   i = 2
   setTimeOut(()=>console.log(i,1);
}
Run Code Online (Sandbox Code Playgroud)


Tus*_*ahi 4

如果var您总是引用相同的变量地址,因为它var是函数作用域的。

let是块作用域的。对于 setTimeout 中的每个回调,您都有不同的值i,因为它是不同的块。

读