01.09.2019, 22:50
|
Интересующийся
|
|
Регистрация: 03.08.2019
Сообщений: 17
|
|
Замыкания в цикле
Здравствуйте. Помогите разобраться с такой проблемой. Есть код:
for(var i = 0; i <= 5; i++){
setTimeout () {function(){
console.log(i)
}, i*1000}
}
1. Почему когда происходит выполнение кода, в консоль записывается 5 шестерок, а тайм-аут срабатывает именно каждую секунду? Тут вопрос не про вывод на консоль шетерок, а именно работа тайм-аута. Каким образом секунда увеличивается постепенно, если логически, после выполнения цикла, вместо i * 1000 должно подставиться 6 * 1000?
Следующий код:
for(var i = 0; i <= 5; i++){
(function(){
var j = i;
setTimeout () {function(){
console.log(j)
}, j*1000}
}())
}
2. Почему при таком варианте, каким-то образом каждая итерация запоминается в переменную j? Я понимаю что это замыкание и тайм-аут видит по замыканию сохраненной значение в переменной j каждый раз. Но вот как это происходит? Вот цикл завершился и записал в j шестерку. А как тайм-аут запомнил, что там были числа 0,1 и т.д.?
Или тут работает отложенный вызов или какой-то стек создается? Тоесть сначало в j присваивается 0 а затем подставляется в тайм-аут функцию и она выполняется и так далее. Но ведь цикл завершается гораздо быстрее чем 1 секунда.
Помогите разобраться.
|
|
01.09.2019, 22:56
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,109
|
|
Сообщение от Devero97
|
Почему когда происходит выполнение кода, в консоль записывается 5 шестерок,
|
лечится заменой var на let,
потому что переменная i одна на весь код, и каждом таймере одна и таже, со значением на момент окончания for
Сообщение от Devero97
|
Почему при таком варианте, каким-то образом каждая итерация запоминается в переменную j?
|
потому что переменная j, всякий раз новая и у каждого таймера своя.
|
|
01.09.2019, 23:22
|
Интересующийся
|
|
Регистрация: 03.08.2019
Сообщений: 17
|
|
Сообщение от рони
|
лечится заменой var на let,
|
Я про это знаю, мне интересно как работает с переменной var.
Сообщение от рони
|
потому что переменная i одна на весь код, и каждом таймере одна и таже, со значением на момент окончания for
|
Как тогда код работает? В цикле 0 меньше 5, значение увеличивается и 1 подставляется в i * 1000, но не успевает попасть во внутрь кода анонимной функции тайм-аута? Цикл уже как-то завершился и туда попала шестерка. Может сможете привести пример проще?
Сообщение от рони
|
потому что переменная j, всякий раз новая и у каждого таймера своя.
|
Почему она всегда новая? Разве туда не сохраняется значения выполненного цикла? А именно 6?
|
|
01.09.2019, 23:56
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,109
|
|
Сообщение от Devero97
|
Почему она всегда новая? Разве туда не сохраняется значения выполненного цикла? А именно 6?
|
потому что функция в строке 2 получила данные и отработала, никаких новых данных в неё не поступит.
var j = i; j получила значение(число в данном случае) переменной i, но знать не знает о переменной i.
в первом варианте идет ссылка на саму переменную, а не на её значение, какое значение имеет i на момент вывода консоли, то и покажет.
может чем поможет, Пример ошибочного использования
|
|
02.09.2019, 10:12
|
Профессор
|
|
Регистрация: 07.11.2013
Сообщений: 458
|
|
Сообщение от Devero97
|
Есть код:
|
И он неправильный, правильно так:
for(var i = 0; i <= 5; i++){
setTimeout(function() { console.log(i) }, i * 1000);
}
Сообщение от Devero97
|
А как тайм-аут запомнил, что там были числа 0,1 и т.д.?
|
Тайм-аут ничего не запоминает, запоминает функция, которая создана и передана ему аргументом function() { console.log(i) }. Функции запоминают переменные окружения в месте своего создания, а не в месте своего вызова. Тайм-аут когда-то потом ее вызовет, но это уже не важно.
Последний раз редактировалось Rise, 02.09.2019 в 10:16.
|
|
02.09.2019, 10:33
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,109
|
|
Сообщение от Rise
|
И он неправильный, правильно так:
|
может забыли аргумент добавить???
|
|
02.09.2019, 10:37
|
|
Профессор
|
|
Регистрация: 25.10.2016
Сообщений: 1,012
|
|
я на собеседованиях люблю "охладить пыл" собеседующих вот таким вариантом
for(var i = 0; i <= 5; i++){
setTimeout(function(i) { console.log(i) }, i * 1000, i);
}
|
|
02.09.2019, 12:21
|
|
Профессор
|
|
Регистрация: 08.11.2017
Сообщений: 642
|
|
let v = 4 // примитив
let w = { v: 4 } // объект
function a(v) { // v передается значением
return {
b: function () {
console.log('b', v++) //4
return this
},
c: function () {
this.b() //5
return this
},
d: function (w) { // w передается ссылкой
console.log('d', w.v++) // 4
}
}
}
a(v).b().c().d(w)
console.log('-', v) // 4
console.log(w) // { v: 5 }
наверное, стоит рассмотреть и такой пример )
|
|
02.09.2019, 12:32
|
|
Профессор
|
|
Регистрация: 25.10.2016
Сообщений: 1,012
|
|
Сообщение от SuperZen
|
w передается ссылкой
|
w тоже передается значением.
просто это сама по себе ссылка на объект, потому внутри объекта можем что-то менять
но сделав, например, w = null внутри метода, мы не поменяем внешнюю w. Потому что она была передана по значению
|
|
02.09.2019, 12:43
|
|
Профессор
|
|
Регистрация: 08.11.2017
Сообщений: 642
|
|
Alexandroppolus, хорошее замечание )
|
|
|
|