2017-02-07 2 views
0

У меня возникла проблема с циклом forTimeout.javascript for-loop и функция тайм-аута

for (var x = 0; x < 5; x++) { 
 
    var timeoutFunction = function() { 
 
     return function() { 
 
      console.log(x) 
 
     } 
 
    } 
 
    setTimeout(timeoutFunction(), 1) 
 
}

Я ожидаю выходного

Тем не менее, по какой-то причине они все выходные 5.

Переменная x определяется в локальной области видимости из-за петли, так что я подумал, что это не может рассчитывать на обратный вызов от SetTimeout. Я тестировал с определением x вне цикла for.

var x = 10 
 
for (var x = 0; x < 5; x++) { 
 
    var timeoutFunction = function() { 
 
     return function() { 
 
      console.log(x) 
 
     } 
 
    } 
 
    setTimeout(timeoutFunction(), 1) 
 
}

я понял, этот вывод будет давать 10, но это не так. Тогда я подумал, что имеет смысл определить x впоследствии.

for (var x = 0; x < 5; x++) { 
 
    var timeoutFunction = function() { 
 
     return function() { 
 
      console.log(x) 
 
     } 
 
    } 
 
    setTimeout(timeoutFunction(), 1) 
 
} 
 
var x = 10

Это делает возвращение только 10. Это означает, что обратные вызовы все называют после того, как для цикла выполняется? И почему они соответствуют только родительской области цикла for после того, как переменная инициализируется после выполнения цикла for? Я что-то упускаю?

Я знаю, как сделать этот пример работы с

for (var x = 0; x < 5; x++) { 
 
    var timeoutFunction = function(x) { 
 
     return function() { 
 
      console.log(x) 
 
     } 
 
    } 
 
    setTimeout(timeoutFunction(x), 1) 
 
}

Тем не менее, я действительно интересно, что не хватает ...

ответ

1

Обратите внимание, что указание 1 в качестве значения задержки фактически не приведет к выполнению функции e через 1 миллисекунду.Самая быстрая функция, которую может выполнить функция, составляет примерно 9 миллисекунд (и это зависит от внутренних компонентов браузера), но на самом деле она не может запускать эту функцию до тех пор, пока не будет запущен другой код.

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

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

Сделайте копию x и пусть каждая функцию использовать свою собственную копию, передавая x в функцию. Когда вы передаете примитивный тип (boolean, number, string), создается копия данных, и это то, что передается. Это нарушает зависимость от общей области x. Ваш последний пример делает это:

for (var x = 0; x < 5; x++) { 
 
    var timeoutFunction = function(x) { 
 
     return function() { 
 
      console.log(x) 
 
     } 
 
    } 
 
    // Because you are passing x into the function, a copy of x will be made for the 
 
    // function that is returned, that copy will be independent of x and a copy will 
 
    // be made upon each loop iteration. 
 
    setTimeout(timeoutFunction(x), 1) 
 
}

Другой пример, который делает то же самое было бы необходимо, если ваша функция тайм-аут не возвращался другую функцию (потому что не было бы никакой функции передать значение к). Таким образом, этот пример создает дополнительную функцию:

for (var x = 0; x < 5; x++) { 
 
    
 
    // This time there is no nested function that will be returned, 
 
    function timeoutFunction(i) {  
 
      console.log(i);   
 
    } 
 
    
 
    // If we create an "Immediately Invoked Funtion Expression" (IIFE), 
 
    // we can have it pass a copy of x into the function it invokes, thus 
 
    // creating a copy that will be in a different scope than x. 
 
    (function(i){   
 
     setTimeout(function(){ 
 
     timeoutFunction(i); // i is now a copy of x 
 
     }, 1); 
 
    }(x)); 
 
}

Если вы работаете с браузерами, поддерживающими стандарт ECMAScript 2015, вы можете просто изменить var x декларацию в цикле для let x, так что x получает область уровня блока на каждой итерации цикла:

// Declaring a variable with let causes the variable to have "block-level" 
 
// scope. In this case the block is the loop's contents and for each iteration of the 
 
// loop. Upon each iteration, a new "x" will be created, so a different scope from 
 
// the old "x" is what's used. 
 
for (let x = 0; x < 5; x++) { 
 
    var timeoutFunction = function() { 
 
     return function() { 
 
      console.log(x) 
 
     } 
 
    } 
 
    setTimeout(timeoutFunction(), 1) 
 
}

+0

Спасибо за удивительные примеры и ссылки! Во-первых, я не мог полностью понять замечание о закрытии. Ссылка сделала это более понятным, а затем я понял точно, что вы имели в виду :) «Причина этого в том, что назначенная функция - это замыкание: она состоит из определения функции и захваченной среды из области действия. созданный циклом, но каждый из них имеет одну и ту же единую среду, в которой есть переменная с изменяющимся значением 'x'. Когда выполняются обратные вызовы setTimeout, доступ к' x' в этот момент вызывает это поведение " – jervtub

+0

@jervtub Добро пожаловать. Закрытие - это, как правило, одна из самых сложных вещей, чтобы раздобыть свой разум, и они понимают немного практики. Не забывайте отмечать как ответ. Удачи! –

1

Вы должны создать новые возможности для функции к сделать это «помнить» значение от данной итерации:

for (var x = 0; x < 5; x++) { 
    var timeoutFunction = (function(x) { 
     return function() { 
      console.log(x) 
     } 
    })(x) 
    setTimeout(timeoutFunction(), 1) 
} 

Другим решением является использование ES2015 let:

for (let x = 0; x < 5; x++) { 
    var timeoutFunction = function() { 
      console.log(x) 
     } 
    setTimeout(timeoutFunction(), 1) 
} 
+0

Спасибо за ваш ответ Bartekfr. Я действительно искал конкретную информацию, почему область не сработала внутри цикла for, поэтому я не установил это как принятый ответ. Мне нравится ссылка на 'let x'. – jervtub

1

использовать этот фрагмент

for(let x = 0; x < 5; x++) { 
    var timeoutFunction = function() { 
     console.log(x); 
    }; 
    setTimeout(timeoutFunction, 1); 
}; 
console.log('For loop completed'); 

JavaScript не многопоточный так с вашим кодируют timeoutFunction будет выполняться после того, как цикл будет завершен, так как вы используете глобальная переменная (по отношению к тайм-ауту контекста контекста) окончательный результат печатается

+0

Ваше решение неверно: поскольку timeoutFunction не возвращает функцию 'console.log', вызывается без задержки. Чтобы проверить это, увеличьте время ожидания до 15000, и вы увидите, что число напечатано без задержки. –

+0

исправил код после тестирования – Raj

+0

Спасибо Raj за ваш ответ! Будучи одиночной нитью, я объясняю задержку регистрации после цикла над целыми числами 1000000. :) Мне нравится ссылка на 'let x'. – jervtub

Смежные вопросы