在循环中将函数传递给 setTimeout:总是最后一个值?

IT技术 javascript settimeout
2021-02-08 03:19:22

我正在尝试使用 setTimeout 执行一个匿名函数,我将信息传递给该函数,但遇到了问题。这个(硬编码版本)可以正常工作:

setTimeout(function(){alert("hello");},1000);
setTimeout(function(){alert("world");},2000);

但是我试图从数组中获取 hello 和 world 并将它们传递到函数中,而没有 (a) 使用全局变量,以及 (2) 使用 eval。我知道如何使用 globals 或 eval 来做到这一点,但我怎么能没有。这是我想要做的(但我知道它行不通):

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout( function(){alert(strings[i]);}, delay);
    delay += 1000;
}

当然,strings[i] 将脱离上下文。如何将 strings[i] 传递给没有 eval 或 globals 的匿名函数?

5个回答

这是经常重复的“我如何在闭包中使用循环变量”的问题。

规范的解决方案是调用一个函数,该函数返回一个绑定到循环变量当前值的函数:

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout(
        (function(s) {
            return function() {
                alert(s);
            }
        })(strings[i]), delay);
    delay += 1000;
}

外部定义function(s) { ... }创建一个新的范围,其中s绑定到所提供参数的当前值 - 即strings[i]-内部范围可以使用它

@qwertymk 是的,但我更喜欢只包装回调,而不是回调的注册,尤其是因为这是对 DOM 事件处理程序执行相同操作时通常需要的。
2021-03-17 03:19:22
@qwertymk、@Alnitak:无论采用哪种方法,您都会在循环内反复创建一个新的但相同的函数实例。更好的 IMO 是在循环外创建一个命名函数,并调用它。
2021-03-23 03:19:22
@Alnitak:我认为按照 svinto 的方式来做是很标准的。在不给读者的大脑另一个层次的范围的情况下,它也更容易理解
2021-03-26 03:19:22
该函数不必返回任何内容,您可以将 setTimeout 放在 IIFE
2021-03-29 03:19:22
2021-04-01 03:19:22

只需在 setTimeout 调用周围添加一个范围:

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    (function(s){
        setTimeout( function(){alert(s);}, delay);
    })(strings[i]);
    delay += 1000;
}

您可以编写一个单独的函数来设置超时:

function doTimer(str, delay) {
  setTimeout(function() { alert(str); }, delay);
}

然后从循环中调用它:

var delay = 1000;
for(var i=0;i<strings.length;i++) {
    doTimer(strings[i], delay);
    delay += 1000;
}
@vsync 是的,肯定有用。(对不起,我一开始误解了你。)
2021-03-16 03:19:22
我可能错了,但在我看来它可能会遇到同样的问题。它不会为 str 变量创建一个闭包,或者会吗?无论哪种方式,它都会留下一个全局 doTimer 函数,所以我更喜欢 Alnitak 的答案。不过谢谢。
2021-03-17 03:19:22
哇,我认为这是最佳答案
2021-03-17 03:19:22
它传递引用的副本,所以没问题。原来的问题是所有超时处理程序只共享一个“i”。并且该函数不必是全局的——它可以是其他代码所在的任何函数中的局部函数。
2021-03-21 03:19:22
你为什么不直接通过delay-> (i+1) * 1000在我看来这比额外的变量要好
2021-04-04 03:19:22

虽然不像其他一些答案那样向后兼容,但我想我会抛出另一个选择..这次使用bind()

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout(alert.bind(this, strings[i]), delay);
    delay += 1000;
}

查看它的实际演示

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout( new Function('alert(strings[i]);'), delay);
    delay += 1000;
}