/*Test scope problem*/
for(var i=1; i<3; i++){
//declare variables
var no = i;
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
}
Это предупреждает "установку 1" и "установку 2" как ожидалось, но после тайм-аута это производит "тест 2" дважды - по некоторым причинам, переменная "нет" не сбрасывается после первого цикла...
Я нашел только "ужасное" обходное решение:
/*Test scope problem*/
var func=function(no){
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
}
for(var i=1; i<3; i++){
func(i);
}
Какие-либо идеи, о том, как обходному решению эта проблема более прямым способом? или действительно ли это - единственный путь?
JavaScript не имеет области блока, и объявления переменных поднимаются. Эти факты вместе означают, что ваш код эквивалентен следующему:
var no;
/*Test scope problem*/
for(var i=1; i<3; i++){
//declare variables
no = i;
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
}
К моменту выполнения вашей функции тайм-аута цикл уже давно завершен, а no
сохраняет свое окончательное значение 2.
Способ обойти это будет передавать текущее значение no
в функцию, которая создает новый обратный вызов для каждого вызова setTimeout
. Создание новой функции каждый раз означает, что каждый обратный вызов setTimeout привязан к другому контексту выполнения со своим собственным набором переменных.
var no;
/*Test scope problem*/
for(var i=1; i<3; i++){
//declare variables
no = i;
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout( (function(num) {
return function() {
alert('test '+num);
};
})(no), 500);
}
Мне нравится, что я могу так много сэкономить, выполнив этот ответ .
Если вам нужна помощь в применении этого ответа, дайте мне знать.
Конечно. Давайте посмотрим на ваш исходный код.
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
Видите эту анонимную функцию? Тот, который вы передаете в setTimeout ()
? Он не вызывается до тех пор, пока таймер не сообщит об этом - что на 500 мс является намного позже выхода из цикла.
Вы надеетесь, что нет
будет вычисляться «на месте», но это не так - оно вычисляется во время вызова функции. К этому моменту no
равно 2.
Чтобы обойти это, нам нужна функция, которая выполняется во время итерации цикла, которая сама вернет функцию, которая setTimeout ()
можно использовать так, как мы ожидаем.
setTimeout(function( value )
{
// 'value' is closed by the function below
return function()
{
alert('test ' + value );
}
}( no ) // Here's the magic
, 500 );
Поскольку мы создаем анонимную функцию и немедленно вызываем ее, была создана новая область видимости, в которой мы можем закрыть переменные. И эта область закрывается около значения
, а не нет
. Поскольку value
получает новое (гм) значение для каждого цикла, каждая из этих лямбда-выражений имеет собственное значение - то, которое мы хотим.
Итак, когда срабатывает setTimeout (), он выполняет функцию, возвращенную нашей функцией закрытия.
Я надеюсь, что это объясняет.
По сути, это то же самое, что и ваше исправление, но с использованием другого синтаксиса для корректировки области действия.
/*Test scope problem*/
for (var i = 1; i < 3; i++) {
//declare variables
var no = i;
//verify no
alert('setting ' + no);
//timeout to recheck
(function() {
var n = no;
setTimeout(function() {
alert('test ' + n);
}, 500);
})();
}
В Javascript нет лексической области видимости (цикл for не создает новую область видимости), и ваше решение является стандартным обходным путем. По-другому это можно написать так:
[1, 2].forEach(function(no){
//verify no
alert('setting '+no);
//timeout to recheck
setTimeout(function(){
alert('test '+no);
}, 500);
})
forEach() был введен в ECMAScript 5 и присутствует в современных браузерах, но не в IE. Однако вы можете использовать мою библиотеку для ее эмуляции.