объявление var JavaScript в цикле

/*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);
}

Какие-либо идеи, о том, как обходному решению эта проблема более прямым способом? или действительно ли это - единственный путь?

8
задан Brock Adams 22 December 2017 в 06:26
поделиться

4 ответа

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);
}
13
ответ дан 5 December 2019 в 10:01
поделиться

Мне нравится, что я могу так много сэкономить, выполнив этот ответ .

Если вам нужна помощь в применении этого ответа, дайте мне знать.

РЕДАКТИРОВАТЬ

Конечно. Давайте посмотрим на ваш исходный код.

//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 (), он выполняет функцию, возвращенную нашей функцией закрытия.

Я надеюсь, что это объясняет.

1
ответ дан 5 December 2019 в 10:01
поделиться

По сути, это то же самое, что и ваше исправление, но с использованием другого синтаксиса для корректировки области действия.

/*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);
  })();
} 
2
ответ дан 5 December 2019 в 10:01
поделиться

В 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. Однако вы можете использовать мою библиотеку для ее эмуляции.

1
ответ дан 5 December 2019 в 10:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: