我遇到了运行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)
}
然而,我真的很想知道遗失了什么......
答案 0 :(得分:1)
您必须为您的功能创建新的范围,以便让它记住&#39;来自给定迭代的值:
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)
}
答案 1 :(得分: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上下文),打印最终结果
答案 2 :(得分:1)
请注意,将1
指定为延迟值实际上不会导致函数在1毫秒后执行。函数执行的最快速度大约是9毫秒(这是基于浏览器的内部结构),但实际上,在没有其他代码运行之前,它无法运行该函数。
对于意外输出,您遇到此行为,因为timeoutFunction
包含对在更高范围内声明的x
变量的引用。这会导致在x
变量周围创建 closure ,这样每次运行该函数时,它都不会获得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
,即可{{1}在每次循环迭代时获取块级范围:
x