简体   繁体   English

循环将函数传递给setTimeout:始终是最后一个值?

[英]Passing functions to setTimeout in a loop: always the last value?

I'm trying to use setTimeout to execute an anonymous function that I pass information into and I'm having trouble. 我正在尝试使用setTimeout执行一个匿名函数,该函数会将信息传递给我,但是遇到了麻烦。 This (hard-coded version) would work just fine: 这个(硬编码版本)可以正常工作:

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

But I'm trying to take the hello and world from an array and pass them into the function without (a) using global variables, and (2) using eval. 但是我试图从数组中获取hello和world并将其传递给函数,而无需(a)使用全局变量,以及(2)使用eval。 I know how I could do it using globals or eval, but how can I do it without. 我知道如何使用globals或eval做到这一点,但是如果没有我怎么做。 Here is what I'd like to do (but I know it won't work): 这是我想做的(但我知道这将无法工作):

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

Of course strings[i] will be out of context. 当然,字符串[i]将脱离上下文。 How can I pass strings[i] into that anonymous function without eval or globals? 如何在没有eval或globals的情况下将字符串[i]传递给该匿名函数?

This is the very frequently repeated "how do I use a loop variable in a closure" problem. 这是经常重复出现的“如何在闭包中使用循环变量”问题。

The canonical solution is to call a function which returns a function that's bound to the current value of the loop variable: 规范的解决方案是调用一个函数,该函数返回绑定到循环变量当前值的函数:

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;
}

The outer definition function(s) { ... } creates a new scope where s is bound to the current value of the supplied parameter - ie strings[i] - where it's available to the inner scope. 外部定义function(s) { ... }创建一个新的作用域,其中s绑定到所提供参数的当前值-即strings[i] - 内部作用域可用。

Just add a scope around the setTimeout call: 只需在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;
}

You could write a separate function to set up the timeout: 您可以编写一个单独的函数来设置超时:

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

Then just call that from the loop: 然后从循环中调用它:

var delay = 1000;
for(var i=0;i<strings.length;i++) {
    doTimer(strings[i], delay);
    delay += 1000;
}

Although not as backward compatible as some of the other answers, thought I'd throw up another option.. this time using bind() ! 尽管不像其他答案那样向后兼容,但以为我会抛出另一个选择..这次使用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;
}

View demo of it in action 观看实际演示

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM