在带有 setTimeout 函数的 for 循环中使用 let

Using a let in a for loop with setTimeout function

如果在 javascript 中函数的参数是按值传递的,为什么下面的 console.log() 似乎是通过引用获取输入的?难道每次在 setTimeout 中调用它时不应该只传递 i 的值而不是它的引用吗?

let i;

for (i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

Shouldn't each time it is invoked inside setTimeout just the value of i be passed and not its reference?

是的,这正是正在发生的事情。它将 i 的值按​​值传递给 console.log(i)。但是,当 i 具有其最终值时,您将在整个 for 循环完成后传递该值,因为这是事物 运行:

的顺序
  1. 您声明let i
  2. 您开始 for 循环。
  3. for 循环 运行s 完成,每次迭代都会启动一个额外的计时器。计时器是 non-blocking 并且是异步的,因此它只是启动计时器并继续执行循环。
  4. 然后,稍后(在 for 循环完全完成后),i 的值位于其终端值上,然后计时器开始触发,这会导致它们调用回调并然后输出 console.log(i).

请注意,如果您使 let i 成为 for 循环声明的一部分,您将得到完全不同的结果:

for (let i = 1; i <= 5; i++) {
  // Because let i was inside the for loop declaration,
  // there is a separate copy of i for each iteration of the for loop here
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

因为这里对for循环中的let i做了特殊处理。循环的每次迭代都有自己单独的 i 副本,因此它仍然具有循环迭代时的值 运行 在稍后计时器触发时。

这与您的原始版本完全不同,在原始版本中,整个循环只有一个 i,并且它的值在每次迭代时递增,因此值 i 在每次迭代时都有当计时器触发时,循环 运行 不再可用。

for (let i=1; i<=5; i++) {
    setTimeout( function timer(){
        console.log( i );
    }, i*1000 );
}

这与一个非常重要的概念有关,叫做scope。作用域是您的变量所在的代码段。

在您的示例中,您在 for 循环之前定义了变量。所以这发生在内部:

  • 你创造我
  • i 设置为 1
  • i 在您的 setTimeout() 中被引用(5 次)
  • i 递增 1(5 次)
  • i 被记录了 5 次,都是 6 次,因为值都指向 同一个 i

在我的示例中,i 在 for 循环中是 scoped。这意味着循环的每次迭代都会生成对 i 的新引用。因此,从计算机的角度来看,这些引用指的是不同的变量(即使你的代码将它们全部命名为 i )。

setTimeOut 是一个异步函数。这意味着它不会停止执行其他同步代码。每个控制台日志将等待 1 秒执行。但是当 i 的值变为 6 时,它将打印 5 次。