2012-04-18 82 views
4

我想设置五个交错的函数调用(发生一秒之差)。这部分工作正常。不起作用的是,我无法将值0到4传递到回调函数中。它每次只传递'5'。我似乎无法弄清楚为什么以及如何解决它。如何将变量传递给setTimeout函数?

代码:

​function callback(num) 
{ 
    console.log(num); 
} 

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) 
    setTimeout(function() { callback(i); }, loadDelay); 

结果:

5 
5 
5 
5 
5 

期望的结果:

0 
1 
2 
3 
4 
+0

增量回调的价值。由于setTimeout是异步的,这就是为什么你总是5. – malletjo 2012-04-18 20:37:11

回答

12

那是因为你创建一个封闭。所以你传递给setTimeout的函数共享相同的i实例。在支持的标准(不IE)浏览器,你可以有:

setTimeout(callback, loadDelay, i); 

参见: http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers

否则,你必须真正bind的参数功能:

setTimeout(callback.bind(undefined, i), loadDelay); 

见: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

如果浏览器不支持ES5 bind方法,你可以实现存在于连接垫片,或者手动做这样的事情:

setTimeout(function(index){ 
    return function() { callback(index) } 
}(i), loadDelay); 

但我想说它使用bind更具可读性,它是值得推行的垫片。实际上,你可以使用这样的:https://github.com/kriskowal/es5-shim

要添加ES5功能(如可能),因为不支持ES5原生浏览器。

+0

太棒了!好&短,优雅。适用于Firefox和Chrome。 – CaptSaltyJack 2012-04-18 20:55:26

4

使用lambda /函数表达式来捕获的当前值。例如

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) { 
    var doCall = function (j) { 
    setTimeout(function() { callback(j); }, loadDelay); 
    } 
    doCall(i); 
} 

这里的问题是,世界上只有1 i为循环的所有迭代值。即使您可以在块中声明它们,javascript中的变量也具有函数范围。这意味着i对于整个功能是活着的。

为了说明问题考虑下面的代码执行完全相同,您的样品

var i; 
for (i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) { 
    ... 
} 

我的解决方案工程,因为它引入了一个新的功能,从而为j一个新的变量寿命。这将当前值i保存在setTimeout回调函数中使用

0

setTimeout创建了一些奇怪的范围问题。 Frame.js的目的是要解决一些这样的困惑,这也适用[更新]:

function callback(num) { 
    console.log(num); 
} 

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) { 
    Frame(function(next, i){ 
     setTimeout(function() { callback(i); }, loadDelay); 
     next(); 
    }, i); 
} 
Frame.init(); 
3

您因变量范围需要关闭才能通过i。请查看this articlethis one as well了解有关关闭的一些有用信息。

Live Demo

function callback(num) 
{ 
    console.log(num); 
} 

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) 
    setTimeout((function(num){return function(){ 
      callback(num); 
     } 
    })(i), loadDelay);​