javascript用于循环和超时函数



我面临一个运行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)
}

然而,我真的很想知道缺少了什么。。。

请注意,将1指定为延迟值实际上不会导致函数在1毫秒后执行。该函数执行的最快时间约为9毫秒(这取决于浏览器的内部),但实际上,在没有其他代码运行之前,它无法运行该函数。

至于意外输出,您会遇到这种行为,因为timeoutFunction包含对在更高范围中声明的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)
}

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

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

另一种解决方案是使用ES2015let:

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将在for循环完成后执行,因为您正在使用全局变量(相对于timeoutFunction上下文),最终结果将打印

相关内容

  • 没有找到相关文章