繁体   English   中英

javascript for循环和超时功能

[英]javascript for-loop and timeout function

我遇到了一个运行setTimeout的for循环的问题。

 for (var x = 0; x < 5; x++) { var timeoutFunction = function() { return function() { console.log(x) } } setTimeout(timeoutFunction(), 1) } 

我期望输出

0

1个

3

4

但是,由于某种原因,它们都输出5。

变量x在for循环的本地范围内定义,因此我认为这可能不计入setTimeout的回调中。 我测试了在for循环之外定义x

 var x = 10 for (var x = 0; x < 5; x++) { var timeoutFunction = function() { return function() { console.log(x) } } setTimeout(timeoutFunction(), 1) } 

我认为此输出将给出10,但没有给出。 然后,我认为事后定义x是有意义的。

 for (var x = 0; x < 5; x++) { var timeoutFunction = function() { return function() { console.log(x) } } setTimeout(timeoutFunction(), 1) } var x = 10 

这确实只返回10。这意味着在执行for循环之后都调用了所有回调吗? 并且为什么在执行for循环后初始化变量后,它们仅符合for循环的父作用域? 我想念什么吗?

我知道如何使该示例与

 for (var x = 0; x < 5; x++) { var timeoutFunction = function(x) { return function() { console.log(x) } } setTimeout(timeoutFunction(x), 1) } 

但是,我真的很想知道缺少了什么...

您必须为函数创建新的作用域,以使其在给定的迭代中“记住”值:

for (var x = 0; x < 5; x++) {
    var timeoutFunction = (function(x) {
        return function() {
            console.log(x)
        }
    })(x)
    setTimeout(timeoutFunction(), 1)
}

另一个解决方案是使用ES2015 let

for (let x = 0; x < 5; x++) {
    var timeoutFunction = function() {
            console.log(x)
        }
    setTimeout(timeoutFunction(), 1)
}

使用此片段

for(let x = 0; x < 5; x++) {
    var timeoutFunction = function() {
        console.log(x);
    };
    setTimeout(timeoutFunction, 1);
};
console.log('For loop completed');

JavaScript不是多线程的,因此在您的代码中,由于使用了全局变量(相对于timeoutFunction上下文),因此time函数将在for循环完成后执行。

请注意,将1指定为延迟值实际上不会导致该函数在1毫秒后执行。 该函数可以最快执行大约9毫秒(这基于浏览器的内部情况),但实际上,只有在没有其他代码在运行时,它才能运行该函数。

对于意外输出,您会遇到此现象,因为timeoutFunction包含对在更高范围中声明的x变量的引用。 这将导致在x变量周围创建一个闭包 ,从而使函数每次运行时都不会获得自身的x副本,但是由于在更高范围中声明了x值,因此它正在共享x值。 这是封闭的经典副作用。

有几种方法可以调整语法以解决此问题...

制作x的副本,并通过将x传递给函数来让每个函数使用其自己的副本。 当您传递原始类型(布尔,数字,字符串)时,将创建数据的副本,这就是要传递的内容。 这打破了对共享x范围的依赖。 您的最后一个示例执行此操作:

 for (var x = 0; x < 5; x++) { var timeoutFunction = function(x) { return function() { console.log(x) } } // Because you are passing x into the function, a copy of x will be made for the // function that is returned, that copy will be independent of x and a copy will // be made upon each loop iteration. setTimeout(timeoutFunction(x), 1) } 

如果您的超时函数没有返回另一个函数(因为将没有值传递给该函数),则需要另一个执行相同操作的示例。 因此,此示例创建了一个额外的功能:

 for (var x = 0; x < 5; x++) { // This time there is no nested function that will be returned, function timeoutFunction(i) { console.log(i); } // If we create an "Immediately Invoked Funtion Expression" (IIFE), // we can have it pass a copy of x into the function it invokes, thus // creating a copy that will be in a different scope than x. (function(i){ setTimeout(function(){ timeoutFunction(i); // i is now a copy of x }, 1); }(x)); } 

如果您正在使用支持ECMAScript 2015标准的浏览器,则只需在循环中将var x声明更改为let x ,以便在每次循环迭代时x都获得块级作用域:

 // Declaring a variable with let causes the variable to have "block-level" // scope. In this case the block is the loop's contents and for each iteration of the // loop. Upon each iteration, a new "x" will be created, so a different scope from // the old "x" is what's used. for (let x = 0; x < 5; x++) { var timeoutFunction = function() { return function() { console.log(x) } } setTimeout(timeoutFunction(), 1) } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM