6

我有一个非常琐碎的问题。对于带有 setTimeout 的简单循环,如下所示:

for (var count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + count);
    }, 1000 * count);
}

控制台给出这样的输出:

Count = 3
Count = 3
Count = 3

不知道为什么这样的输出。任何人都可以解释一下,好吗?

4

9 回答 9

6

这与 JavaScript 中如何处理范围和提升有关。

您的代码中发生的情况是 JS 引擎将您的代码修改为:

var count;

for (count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + count);
    }, 1000 * count);
}

setTimeout()它运行时,它会首先在它自己的范围内count查找,但它不会找到它,所以它会开始在函数上查找关闭的函数(这称为闭包),setTimeout直到找到var count语句,这将具有值 3,因为循环将在第一个超时函数执行之前完成。

更多代码解释了您的代码实际上如下所示:

//first iteration
var count = 0; //this is 1 because of count++ in your for loop.

for (count = 0; count < 3; count++) { 
    setTimeout(function() {
        alert("Count = " + 1);
    }, 1000 * 1);
}
count = count + 1; //count = 1

//second iteration
var count = 1;

for (count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + 2);
    }, 1000 * 2);
}
count = count + 1; //count = 2

//third iteration
var count = 2;

for (count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + 3);
    }, 1000 * 3);
}
count = count + 1; //count = 3

//after 1000 ms
window.setTimeout(alert(count));
//after 2000 ms
window.setTimeout(alert(count));
//after 3000 ms
window.setTimeout(alert(count));
于 2013-10-07T09:57:31.557 回答
4

这样想:

1000*n 毫秒结束后,count 的值是多少?

当然会是 3,因为 foo 循环的结束时间早于 1000*n 毫秒的超时时间。

为了打印 1,2,3,您需要以下内容:

for (var count = 0; count < 3; count++) {
    do_alert(num);
}

function do_alert(num) {
    setTimeout(function() {
        alert("Count = " + num);
    }, 1000 * num);
}

另一种方法是使其成为(在JavaScript 闭包与匿名函数closure function中有很好的解释)

for (var count = 0; count < 3; count++) {
    (function(num){setTimeout(function() {
        alert("Count = " + num);
    }, 1000 * num)})(count);
}

这两个代码示例实际上将类似地工作。

第一个示例在每次迭代时调用一个命名函数 (do_alert)。

do_alert第二个示例在每次迭代时调用一个 CLOSURE 匿名函数(就像)。

这都是 SCOPE 的问题。

希望有帮助。

于 2013-10-07T10:00:56.753 回答
1

这与闭包范围有关。count每个 setTimeout 回调函数的范围内都有相同的变量。您正在递增它的值并创建一个函数,但该函数的每个实例count在其范围内都有相同的变量,并且在回调函数执行时它的值将是 3。

您需要var localCount = count在循环中的新范围内创建变量的副本(例如 )for以使其工作。由于for不会创建范围(这是整个事情的原因),您需要引入一个具有函数范围的范围。

例如

for (var i = 0; i < 5; i++) {
  (function() {
    var j = i;
    setTimeout(function() {
      console.log(j)
    },
    j*100);
   })();
 }
于 2013-10-07T09:57:16.017 回答
1

想一想:

  1. 该代码执行一个循环,在该循环中它设置一些代码稍后运行。
  2. 循环结束。
  3. setTimeout 代码执行。有什么价值count?循环在很久以前就结束了...
于 2013-10-07T09:55:50.043 回答
1

首先, setTimeout(function, milliseconds) 是一个函数,它在“毫秒”毫秒后执行一个函数。

请记住,JS 将函数视为对象,因此 for(...) 循环最初会产生如下内容:

setTimeout( ... ) setTimeout( ... ) setTimeout( ... )

现在 setTimeout() 函数将一一执行。

setTimeout() 函数将尝试在当前范围内查找计数变量。否则,它将转到外部范围并找到 count,其值已通过 for 循环递增到 3。

现在,开始执行....第一个警报立即显示,因为毫秒为 0,第二个警报在 1000 毫秒后显示,然后第三个警报在 2000 毫秒后显示。他们都显示Count = 3

于 2018-05-26T05:31:30.910 回答
1

这里的简单修复是利用 es6let局部变量。你的代码看起来几乎一样,除了它会做你所期望的:)

 for (let count = 0; count < 3; count++) {
    setTimeout(function() {
        alert("Count = " + count);
    }, 1000 * count);

}

或者您可以创建一个递归函数来完成该工作,如下所示:

function timedAlert(n) {
  if (n < 3) {
    setTimeout(function() {
        alert("Count = " + n);
        timedAlert(++n);
    }, 1000);
  }
}

timedAlert(0);
于 2018-01-25T02:08:02.673 回答
0

这是因为当 for 循环完成执行时,计数为 3,然后调用设置的超时。

尝试这个:

var count = 0; 
setTimeout(function() {
       for (count = 0; count < 3; count++) {
           alert("Count = " + count);
        }
}, 1000* count);
于 2013-10-07T09:58:31.200 回答
0

这是因为所有超时都在循环完成时运行。

然后超时函数采用计数的当前值。

那总是 3 因为 for 循环已经完成。

于 2013-10-07T09:58:22.543 回答
0

在这种情况下,更好的解决方案是“忘记循环和递归”,并使用“setInterval”的这种组合,包括“setTimeOut”:

    function iAsk(lvl){
        var i=0;
        var intr =setInterval(function(){ // start the loop 
            i++; // increment it
            if(i>lvl){ // check if the end round reached.
                clearInterval(intr);
                return;
            }
            setTimeout(function(){
                $(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
            },50);
            setTimeout(function(){
                 // do another bla bla bla after 100 millisecond.
                seq[i-1]=(Math.ceil(Math.random()*4)).toString();
                $("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
                $("#d"+seq[i-1]).prop("src",pGif);
                var d =document.getElementById('aud');
                d.play();                   
            },100);
            setTimeout(function(){
                // keep adding bla bla bla till you done :)
                $("#d"+seq[i-1]).prop("src",pPng);
            },900);
        },1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
    }

PS:了解(setTimeOut)的真实行为:它们都将在同一时间开始“三个bla bla bla将在同一时刻开始倒计时”,因此请设置不同的超时时间来安排执行。

PS 2:计时循环的示例,但是对于反应循环,您可以使用事件,promise async await ..

于 2017-12-01T08:46:19.130 回答