[英]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.