Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Замыкания в цикле (https://javascript.ru/forum/misc/78361-zamykaniya-v-cikle.html)

Devero97 01.09.2019 22:50

Замыкания в цикле
 
Здравствуйте. Помогите разобраться с такой проблемой. Есть код:

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

Цитата:

Сообщение от Devero97
Почему когда происходит выполнение кода, в консоль записывается 5 шестерок,

лечится заменой var на let,
потому что переменная i одна на весь код, и каждом таймере одна и таже, со значением на момент окончания for
Цитата:

Сообщение от Devero97
Почему при таком варианте, каким-то образом каждая итерация запоминается в переменную j?

потому что переменная j, всякий раз новая и у каждого таймера своя.

Devero97 01.09.2019 23:22

Цитата:

Сообщение от рони
лечится заменой var на let,

Я про это знаю, мне интересно как работает с переменной var.
Цитата:

Сообщение от рони
потому что переменная i одна на весь код, и каждом таймере одна и таже, со значением на момент окончания for

Как тогда код работает? В цикле 0 меньше 5, значение увеличивается и 1 подставляется в i * 1000, но не успевает попасть во внутрь кода анонимной функции тайм-аута? Цикл уже как-то завершился и туда попала шестерка. Может сможете привести пример проще?
Цитата:

Сообщение от рони
потому что переменная j, всякий раз новая и у каждого таймера своя.

Почему она всегда новая? Разве туда не сохраняется значения выполненного цикла? А именно 6?

рони 01.09.2019 23:56

Цитата:

Сообщение от Devero97
Почему она всегда новая? Разве туда не сохраняется значения выполненного цикла? А именно 6?

потому что функция в строке 2 получила данные и отработала, никаких новых данных в неё не поступит.
var j = i; j получила значение(число в данном случае) переменной i, но знать не знает о переменной i.

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

Rise 02.09.2019 10:12

Цитата:

Сообщение от Devero97
Есть код:

И он неправильный, правильно так:
for(var i = 0; i <= 5; i++){
    setTimeout(function() { console.log(i) }, i * 1000);
}

Цитата:

Сообщение от Devero97
А как тайм-аут запомнил, что там были числа 0,1 и т.д.?

Тайм-аут ничего не запоминает, запоминает функция, которая создана и передана ему аргументом function() { console.log(i) }. Функции запоминают переменные окружения в месте своего создания, а не в месте своего вызова. Тайм-аут когда-то потом ее вызовет, но это уже не важно.

рони 02.09.2019 10:33

Цитата:

Сообщение от Rise
И он неправильный, правильно так:

может забыли аргумент добавить???

Alexandroppolus 02.09.2019 10:37

я на собеседованиях люблю "охладить пыл" собеседующих вот таким вариантом :)
for(var i = 0; i <= 5; i++){
    setTimeout(function(i) { console.log(i) }, i * 1000, i);
}

SuperZen 02.09.2019 12:21

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 }


наверное, стоит рассмотреть и такой пример )

Alexandroppolus 02.09.2019 12:32

Цитата:

Сообщение от SuperZen
w передается ссылкой

w тоже передается значением.
просто это сама по себе ссылка на объект, потому внутри объекта можем что-то менять

но сделав, например, w = null внутри метода, мы не поменяем внешнюю w. Потому что она была передана по значению

SuperZen 02.09.2019 12:43

Alexandroppolus, хорошее замечание )


Часовой пояс GMT +3, время: 15:08.