объявление вар JavaScript в цикле - PullRequest
8 голосов
/ 28 апреля 2010
/*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);
}

Есть идеи о том, как обойти эту проблему более прямым способом? или это единственный способ?

Ответы [ 4 ]

13 голосов
/ 28 апреля 2010

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);
}
2 голосов
/ 28 апреля 2010

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

/*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);
  })();
} 
1 голос
/ 28 апреля 2010

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 голос
/ 28 апреля 2010

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

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

EDIT

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

//timeout to recheck 
setTimeout(function(){
    alert('test '+no);
}, 500);

Видите эту анонимную функцию? Тот, который вы передаете в setTimeout()? Он не вызывается до тех пор, пока таймер не сообщит об этом - что при 500 мс равно через много секунд после , когда цикл завершился.

Вы надеетесь, что no будет оцениваться "на месте", но это не так - он будет оцениваться во время вызова функции. К этому моменту 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, а не no. Поскольку value получает новое значение (ahem) для каждого цикла, каждый из этих lambdas имеет свое собственное значение - то, которое мы хотим получить.

Таким образом, когда срабатывает setTimeout (), он выполняет функцию, возвращенную нашей функцией закрытия.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...