Переменные глобальные вопросы понимания области - PullRequest
5 голосов
/ 18 октября 2010

мой вопрос на самом деле состоит в понимании - у меня есть рабочее решение, я просто не понимаю, как оно работает.

Хорошо, так что я пытаюсь сделать, это добавить setTimeout вцикл, и передача изменяющегося значения через него.Пример:

for (i=0;i<11;i++)
{
     setTimeout("alert(i)",1000);
}

Если я правильно понял, это не работает, потому что Javascript не (как PHP) передает значение i в функцию, но передает ссылку на i - которая, в свою очередь, не является статической,но продолжает меняться со счетчиком.

Я нашел решение, которое выглядит так:

for (i=0;i<11;i++)
{
    setTimeout(function(x){return function(){alert(x)};}(i),1000);
}

Я не очень понимаю, что это на самом деле делает.Похоже, что она передает функцию «alert» обратно вызывающей функции, но я не могу понять этого.

Я могу работать с этим решением, а также адаптировать его к другим контекстам, но яМне бы очень хотелось понять весь мой код, а не просто использовать вещи, которые я где-то нашел, и быть счастливым, что это работает.И, кроме того, я ищу более тонкую версию для достижения той же цели.

Спасибо, Марко

Ответы [ 3 ]

4 голосов
/ 18 октября 2010

Что это делает:

function(x){return function(){alert(x)};}(i)

Это принимает функцию:

function(x){ ...code... }

И выполняет ее немедленно , передавая i (из for loop) в качестве единственного параметра (для этого и используется (i) в конце).Эта возвращает другую функцию:

function(){ alert(x); }

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

2 голосов
/ 18 октября 2010

Причина, по которой вы вызываете функцию, которая возвращает функцию, заключается в том, что вам нужно иметь какой-то способ, чтобы функция передавалась в setTimeout(), чтобы иметь ссылку на current значение i.

Поскольку код ожидает запуска в течение 1000 мс, цикл for будет завершен до его запуска, и значение, если i будет равно 11.

Но поскольку функция имеетВ своей собственной области видимости вы можете передать значение i в функцию, которая вызывается немедленно, так что на нее ссылается локальная переменная x, на которую возвращаемая функция может ссылаться, когда setTimeout() наконец вызывает ее.

for (i=0; i<11; i++) {
    setTimeout(function(x){
                 // CONTINUE HERE:
                 // x is a local variable to the function being executed
                 //    which references the current value of i

                 // A function is being returned to the setTimeout that
                 //    references the local x variable
                 return function(){ alert(x); };

               }(i) // START HERE:
                    // The "outer" function is executed immediately, passing the
                    //   current value of "i" as the argument.
     ,1000);
}

Итак, вы получите эквивалент, который будет выглядеть примерно так:

setTimeout( function(){ alert(x); }, 1000); //...where x === 0
setTimeout( function(){ alert(x); }, 1000); //...where x === 1
setTimeout( function(){ alert(x); }, 1000); //...where x === 2
setTimeout( function(){ alert(x); }, 1000); //...where x === 3
// etc.
0 голосов
/ 19 октября 2010

Патрик и Ник очень помогли мне в понимании всего этого, поэтому я хотел бы подвести итог для всех, кто сталкивается с той же проблемой, что и я:

setTimeout (а также с некоторыми другими задержками по временитакие функции, как EventListeners), кажется, сохраняет обратный вызов как строку, а затем использует какой-то внутренний eval в этой строке, таким образом интерпретируя его как код.

Это вызывает проблемы с циклами и функциями с задержкой, так какссылка на переменную ссылается на конечный результат этого цикла или, возможно, на переменную, которая даже не является глобальной.

Насколько я понимаю, решение с помощью функции в функции решает эту проблему, даваястрока обратно как результат функции, которая затем содержит значение, а не ссылку на переменную (alert("1") не alert(i)).

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

for (i=0;i<11;i++)
{
     setTimeout("alert("+i+")",1000);
}

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

Еще раз спасибо Патрику, Нику и неизвестному парню, который снял свой ответчто нашли время помочь мне с этим!

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