为什么这个/ setTimeout()代码实际上输出的数字从0到9?

时间:2014-10-09 17:59:53

标签: javascript for-loop asynchronous closures settimeout

JavaScript闭包的一个常见缺陷是running setTimeout() from a for loop,并且期望计数器在每次迭代时以不同的值传递,而实际上它会在setTimeout()函数执行之前被赋值为最后一个值:< / p>

&#13;
&#13;
for (i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100);
}  // => prints "10" 10 times
&#13;
&#13;
&#13;

对此的一个解决方案是立即调用函数表达式:

&#13;
&#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;
&#13;
&#13;

另一种方法是传递额外的callback argument to setTimeout()(在IE&lt; 9中不起作用):

&#13;
&#13;
for (i = 0; i < 10; i++) {
  setTimeout(function foo(n) {
    console.log(n)
  }, 100, i);
}
&#13;
&#13;
&#13;

但为什么以下最简单的代码产生相同的结果(0, 1, 2, ... 9)?

&#13;
&#13;
for (var i = 0; i < 10; i++)
  setTimeout(console.log(i), 100);
&#13;
&#13;
&#13;

3 个答案:

答案 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,您会发现差异更大,因此日志消息应该以半秒的间隔延迟。