Подтвердить что ты не робот

Передача функций в setTimeout в цикле: всегда последнее значение?

Я пытаюсь использовать setTimeout для выполнения анонимной функции, в которую я передаю информацию, и у меня возникают проблемы. Эта (жестко запрограммированная версия) будет работать очень хорошо:

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

Но я пытаюсь взять hello и world из массива и передать их в функцию без (a) с использованием глобальных переменных и (2) с помощью eval. Я знаю, как это сделать, используя глобальные переменные или eval, но как я могу это сделать без этого. Вот что я хотел бы сделать (но я знаю, что это не сработает):

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

Конечно, строки [i] будут вне контекста. Как передать строки [i] в ​​эту анонимную функцию без eval или globals?

4b9b3361

Ответ 1

Это очень часто повторяющаяся "как использовать проблему с циклом в замыкании".

Каноническое решение состоит в вызове функции, которая возвращает функцию, которая привязана к текущему значению переменной цикла:

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] - где он доступен для внутренней области.

Ответ 2

Просто добавьте область вокруг вызова 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;
}

Ответ 3

Вы можете написать отдельную функцию для установки таймаута:

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

Ответ 4

Несмотря на то, что он не был совместим с предыдущими версиями, как некоторые из других ответов, я подумал, что я выбрал еще один вариант. На этот раз с помощью 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;
}

Открыть демо-версию в действии

Ответ 5

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