在带有 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
循环完成后传递该值,因为这是事物 运行:
的顺序
- 您声明
let i
。
- 您开始
for
循环。
for
循环 运行s 完成,每次迭代都会启动一个额外的计时器。计时器是 non-blocking 并且是异步的,因此它只是启动计时器并继续执行循环。
- 然后,稍后(在
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 次。
如果在 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
循环完成后传递该值,因为这是事物 运行:
- 您声明
let i
。 - 您开始
for
循环。 for
循环 运行s 完成,每次迭代都会启动一个额外的计时器。计时器是 non-blocking 并且是异步的,因此它只是启动计时器并继续执行循环。- 然后,稍后(在
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 次。