JavaScript闭包的一个常见缺陷是running setTimeout()
from a for
loop,并且期望计数器在每次迭代时以不同的值传递,而实际上它会在setTimeout()
函数执行之前被赋值为最后一个值:< / p>
for (i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100);
} // => prints "10" 10 times
&#13;
对此的一个解决方案是立即调用函数表达式:
for (i = 0; i < 10; i++)
(function(j) {
setTimeout(function foo() {
console.log(j)
}, 100);
})(i); // prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
&#13;
另一种方法是传递额外的callback argument to setTimeout()
(在IE&lt; 9中不起作用):
for (i = 0; i < 10; i++) {
setTimeout(function foo(n) {
console.log(n)
}, 100, i);
}
&#13;
但为什么以下最简单的代码产生相同的结果(0, 1, 2, ... 9
)?
for (var i = 0; i < 10; i++)
setTimeout(console.log(i), 100);
&#13;
答案 0 :(得分:1)
这种显然令人惊讶的行为是因为first parameter to setTimeout
可以是函数和字符串,后者是eval()
- 作为代码。
因此setTimeout(console.log(i), 100);
会立即执行console.log(i)
,返回undefined
。然后执行setTimeout("", 100)
,在100分钟后进行NOP调用(或由引擎优化)。
答案 1 :(得分:0)
只是为了咧嘴笑,你可以做的另一件事情(当你有.bind()
时)是
for (i = 0; i < 10; i++) {
setTimeout(function () {
var i = +this;
console.log(i)
}.bind(i), 100);
}
比IIFE少一点点。
答案 2 :(得分:0)
为什么以下最简单的代码产生相同的结果(0,1, 2,... 9)?
for (var i = 0; i < 10; i++) setTimeout(console.log(i), 100);
因为它实际上并没有。如果仔细观察,您会注意到日志消息在出现在控制台中之前不需要十分之一秒。通过立即调用console.log(i)
,您只会将调用结果(undefined
)传递给setTimeout
,后者将 。实际上,代码等同于
for (var i = 0; i < 10; i++) {
console.log(i);
setTimeout(undefined, 100);
}
如果您在所有代码段中将100
替换为i*500
,您会发现差异更大,因此日志消息应该以半秒的间隔延迟。