Передача функций в 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?
Ответы
Ответ 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;
}