6

我正在尝试设置五个交错的函数调用(相隔一秒)。那部分工作正常。不起作用的是,我不能将值 0 到 4 传递给回调函数。它每次只通过'5'。我似乎无法弄清楚为什么以及如何解决它。

代码:

​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
4

4 回答 4

12

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

setTimeout(callback, loadDelay, i);

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

Otherwise you have to actually bind the argument to the function:

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

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

If the browser doesn't support ES5 bind method, you can either implement the shim present in the link above, or manually doing something like:

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

But I would say it's more readable using bind and it's worthy to implement the shim. You can actually use this: https://github.com/kriskowal/es5-shim

To add es5 capabilities (where is possible) in the browser that don't support es5 natively.

于 2012-04-18T20:52:32.510 回答
6

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

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

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

为了说明问题,请考虑以下代码的执行与您的示例完全相同

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

我的解决方案有效,因为它引入了一个新函数,因此为j. 这将保存函数中的当前值以isetTimeout回调中使用

于 2012-04-18T20:40:46.413 回答
4

i由于变量作用域,您需要一个闭包才能通过。查看这篇文章,以及这篇文章,获得一些关于闭包的好信息。

现场演示

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);​
于 2012-04-18T20:42:20.687 回答
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();
于 2012-04-18T20:41:52.987 回答