引擎盖下发生了什么?循环中的Javascript计时器

时间:2011-11-19 20:54:14

标签: javascript timer settimeout

有人可以清楚地分解这里发生的事情吗?

function timerCheck() {
    for(var i=0; i<5; i++) {
        setTimeout(function() {
            console.log("Hello" + i);
        }, 3000);
    }
}

因此,有些人可能知道,调用此函数将无法按预期工作。最终会发生的是,这个函数将一次调用5次,每次设置为5。这将是3秒后的输出:

Hello5
Hello5
Hello5
Hello5
Hello5

我也明白使用setInterval方法是解决这类问题的正确方法,但我很好奇这里发生了什么。我真的想了解Javascript的工作原理。请注意,我没有计算机科学背景,只是一个自学成才的编码器。

3 个答案:

答案 0 :(得分:2)

这可能有助于您更好地了解正在发生的事情:

function timerCheck() {
    for(var i=0; i<5; i++) {
        console.log("Hi" + i);
        setTimeout(function() {
            console.log("Hello" + i);
        }, 3000);
        console.log("Bye" + i);
    }
}

你会看到

Hi0
Bye0
Hi1
Bye1
Hi2
Bye2
Hi3
Bye3
Hi4
Bye4

立即打印到控制台,因为循环的所有五次迭代都很快完成,然后在五秒后你会看到:

Hello5
Hello5
Hello5
Hello5
Hello5

因为超时(大约在同一时间设置)都会立即发生,并且因为循环已经完成:i == 5

这是由i的范围引起的。变量itimerCheck();中声明之后无处不在。在setTimeout集中,匿名函数中没有本地i,没有var ii不是作为函数的参数给出。

您可以使用闭包轻松解决此问题,闭包将返回具有i:

的本地副本的函数
function timerCheck() {
    for(var i=0; i<5; i++) {
        setTimeout((function(loc_i) {
            return function() {
                console.log("Hello" + loc_i);
            };
        })(i), 3000);
    }
}

将输出:

Hello0
Hello1
Hello2
Hello3
Hello4

要理解这一点:

(function(loc_i) {
    return function() {
        console.log("Hello" + loc_i);
    };
})(i)

你必须知道一个函数可以在Javascript中立即执行。 IE浏览器。 (function(x){ console.log(x); })('Hi');Hi打印到控制台。所以上面的外部函数只接受一个参数(i的当前值)并将其存储到名为loc_i的函数的局部变量中。该函数立即返回一个将"Hello" + loc_i打印到控制台的新函数。这是传递给超时的函数。

我希望一切都有意义,如果你还不清楚某事,请告诉我。

答案 1 :(得分:1)

JavaScript中的变量范围仅限于函数。

在您的示例中,变量itimerCheck内声明。这意味着在循环结束时,i将等于5

现在,添加对setTimeout的调用不会改变i的作用域为timerChecki已修改为{{1}的事实到每个5调用中的代码运行时。

您可以创建一个“捕获”setTimeout值的函数,这样当您从循环内部调用它时,您将获得i调用的新变量范围:

setTimeout

由于function createTimer(j) { setTimeout(function() { console.log("Hello" + j); }, 3000); } function timerCheck() { for(var i=0; i<5; i++) { createTimer(i); } } 采用参数createTimer,当您将ji中的for循环传递到timerCheck时,createTimer为现在限定为j,以便每个createTimer调用都有自己的setTimeout

答案 2 :(得分:0)

这实际上是安德鲁斯回答的补充

如果您尝试设置一个设置输出的变量,它也会解释范围。

function test()
{
    for(var i=0; i<5; i++) {
    t = "Hello " + i + "<br/>";
    document.write(t);
        setTimeout(function() {
            document.write(t);
        }, 3000);
    }
}

正如您所看到的那样,写入将按预期进行,但在setTimeout触发时,t变量将是最后一次设置。哪个是Hello 4.

所以输出将是

Hello 0
Hello 1
Hello 2
Hello 3
Hello 4

来自循环和

Hello 4
Hello 4
Hello 4
Hello 4
Hello 4
来自setTimeout