如何将变量传递到 setTimeout 函数中

How to pass a variable into a setTimeout function?

本文关键字:setTimeout 函数 变量      更新时间:2023-09-26

我正在尝试设置五个交错的函数调用(相隔一秒)。这部分工作正常。不起作用的是,我无法将值 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

那是因为你创建了一个闭包。因此,您传递给的函数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 功能(如果可能)。

使用 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回调中使用

由于变量作用域,您需要一个闭包才能通过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);​

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();