JavaScript中'for'循环中的'setTimeOut'调用为什么会失败?

24 浏览
0 Comments

JavaScript中'for'循环中的'setTimeOut'调用为什么会失败?

这个问题已经有答案了

在JavaScript中的for循环内异步处理[重复]

循环中的JavaScript闭包 - 简单实用的例子

让我澄清一下我的问题。我不是在问如何使以下代码运行。我知道可以使用let关键字或一个捕获自己的值i的 iffe。我只需要澄清以下代码中访问i的值的方式。我读了一篇有关以下代码为什么不起作用的博客文章。 博客文章

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

作者声称该代码将不起作用,因为我们将变量i作为引用而不是值传递。也就是说,我们不是在每次迭代中提供i的值,而是在setTimeout回调中将变量作为引用提供给回调函数。实际上,当循环终止并且回调触发时,我们将具有对变量i的引用,它将是6。这就是它的工作原理吗?

以下是我的理解。我的理解是,在执行循环时,我们不会将任何东西“传递”到setTimeout函数的回调中。我们只是设置了异步调用。当闭包回调函数执行时,它们会根据词法作用域规则查找变量i。也就是说,闭包会在回调有封闭作用域的范围内查找变量,而在这种情况下,因为它是在for循环完成后执行的,所以它是6。

这是哪个原因导致函数将i的值解析为6呢?是由于每次迭代时传递的变量作为引用变量,还是由于词法作用域?

admin 更改状态以发布 2023年5月24日
0
0 Comments

让我用你的代码来解释:

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

当函数 setTimeout() 被触发时, 变量 i 的值将会从 1, 2, 3, 4, 5 依次增加, 直到 i 的值增加到 6 并停止 for 循环。

   var i = 1;
   setTimeout(function() { console.log(i); }, 1000*1);
   i++;
   setTimeout(function() { console.log(i); }, 1000*2);
   i++;
   setTimeout(function() { console.log(i); }, 1000*3);
   i++;
   setTimeout(function() { console.log(i); }, 1000*4);
   i++;
   setTimeout(function() { console.log(i); }, 1000*5);
   i++;
   // Now i = 6 and stop the for-looping.

一段时间后, 会触发 timeout 的回调函数, 并打印出 i 的值 。就像上面所说的那样, i 的值已经等于 6 了。

    console.log(i) // i = 6 already.
    console.log(i) // i = 6 already.
    console.log(i) // i = 6 already.
    console.log(i) // i = 6 already.
    console.log(i) // i = 6 already.

原因在于缺少 ECMAScript 5 中的 块级作用域(var i = 1;i <=5 ;i++) 将创建一个变量, 它会在整个函数中存在, 并且可以在本地作用域或闭包中被修改。这就是为什么 ECMAScript 6 中有 let 关键字的原因。

可以通过将 var 更改为 let 来轻松解决此问题:

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

0
0 Comments

你说的词法作用域是导致这个行为的原因。当计时器函数运行时(在当前正在运行的代码完成后),它们尝试解析i,必须查找作用域链以找到它。由于词法作用域,i只在作用域链中存在一次(比计时器函数高一级),而此时i6,因为此时循环已经终止。

var关键字使JavaScript中的变量具有函数或全局作用域(基于声明的位置)。在你的代码中,var i导致i变量存在于全局(因为你的代码不在函数内),每个计时器函数在最终运行时都必须解析同一个i。由于计时器函数等到循环完成后才运行,所以i为循环导致它的最后一个值(6)。

var i更改为let i创建块级作用域以解决问题。

let为变量创建块级作用域。在每次循环迭代中,您再次进入循环块并为i创建一个独立的作用域,每个计时器函数都能独立使用。

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

这段内容是一个包含了一个粗体文本的段落。

0