Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Замыкание. В учебнике не разбирают один момент. (https://javascript.ru/forum/misc/78159-zamykanie-v-uchebnike-ne-razbirayut-odin-moment.html)

Devero97 03.08.2019 19:51

Замыкание. В учебнике не разбирают один момент.
 
Здравствуйте. Объясните пожалуйста, почему в теме про замыкание не объясняется то, что функция присваивается переменной?
https://learn.javascript.ru/article/...ecounter-2.svg

Почему я при таком вызове makeCounter()() (без переменной) не получаю нужного результата?

Каким образом в таком случае я постоянно получаю единицу, а с переменной результат сохраняется и выводит его корректный?

Где-то читал, что создается образец функции, но как он создается по какому правилу?

Белый шум 03.08.2019 21:06

Каждый новый вызов функции makeCounter() делает следующее:
1) создаёт локальную переменную count и присваивает ей значение 0.
2) возвращает функцию, которой доступна только что созданная переменная count.

Т.о., если вы несколько раз вызовете makeCounter() и сохраните её результат в разных переменных, то вы получите несколько независимых счётчиков:
let counter1 = makeCounter();
let counter2 = makeCounter();

А если вы не сохранили результат действия ф-ии makeCounter() в переменной, то соответствующий контекст сразу же теряется. Именно это и происходит при немедленном вызове makeCounter()() (следующий такой же вызов делает всё заново, т.е. значение счётчика count снова будет равно нулю).

Devero97 03.08.2019 21:30

Хм.. А каким образом сохраняется контекст в переменную?

Как происходит запоминание увеличенного значения?

Разве это не одно и тоже:
makeCounter()();
 let counter = makeCounter();
 counter()


Мы же для переменно присвоили ссылку на функцию, только внутреннюю. И когда вызываем counter(), то происходит по сути одно и тоже, если бы мы вызывали makeCounter()() - counter()().

В чем тут может быть отличие?

Белый шум 04.08.2019 05:25

Devero97,
Для каждой функции в джаваскрипте доступны все её собственные переменные, а также переменные всех функций в которые она вложена. А вот переменные соседних функций ей недоступны.

Ф-я, которую возвращает makeCounter(), вложена в этот самый makeCounter(), поэтому ей доступны её переменные (count). Но при этом переменная count не является частью возвращаемой ф-ии, поэтому её значение сохраняется между вызовами этой вложенной ф-ии (если результат makeCounter() сохранить в переменную, а потом вызвать несколько раз).

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

function makeCounter(){
 let count = 0;
 return function(){
  console.log(count++);
 }
}

let counter1 = makeCounter();
let counter2 = makeCounter();

console.log("Трижды вызываем counter1():");
counter1();
counter1();
counter1();

console.log("Дважды вызываем counter2():");
counter2();
counter2();



Если вы вызываете внутреннюю функцию сразу - makeCounter()(); то вы не можете вызвать тот же самый объект второй раз, поскольку не сохранили ссылку на него. И следующий вызов makeCounter()(); сначала вернёт новый объект, а затем вызовет его как функцию. Т.е. такие вызовы будут всегда возвращать ноль.

Белый шум 04.08.2019 05:42

Цитата:

Сообщение от Devero97 (Сообщение 511170)
Хм.. А каким образом сохраняется контекст в переменную?

Как происходит запоминание увеличенного значения?

Давайте немного перепишем пример:
function makeCounter2(){
 let count = 0;
 function plus(){
  console.log(count++);
 }
 plus();
 plus();
 plus();
}

makeCounter2();

Так понятней как происходит запоминание увеличенного значения?

Замыкание делает абсолютно то же самое, только ссылка на внутреннюю функцию plus() попадает за пределы ф-ии makeCounter2() и может вызываться оттуда.

Но новый вызов makeCounter2() выведет три числа опять с нуля, т.к. первой строкой создаётся новая переменная count со значением ноль. Конструкция makeCounter()() работает ровно так же.

Devero97 04.08.2019 11:46

Цитата:

Сообщение от Белый шум (Сообщение 511183)
Давайте немного перепишем пример:
function makeCounter2(){
 let count = 0;
 function plus(){
  console.log(count++);
 }
 plus();
 plus();
 plus();
}

makeCounter2();

Так понятней как происходит запоминание увеличенного значения?

Замыкание делает абсолютно то же самое, только ссылка на внутреннюю функцию plus() попадает за пределы ф-ии makeCounter2() и может вызываться оттуда.

Но новый вызов makeCounter2() выведет три числа опять с нуля, т.к. первой строкой создаётся новая переменная count со значением ноль. Конструкция makeCounter()() работает ровно так же.


Вот тут и происходит недопонимание. Даже в том примере, который вы переписали, где происходит вызов функции plus() внутри функции makeCounter(). Функция plus() после вызова перезаписывает значение переменной или она только запоминает как-то у себя это значение?

Я понимаю, что она по лексическому окружению не видит локальной переменной и по ссылке идет в другое лексическое окружение, и при обнаружении нужной переменной она ее берет и изменяет в другом лексическом окружении или она эту переменную как-то изменяет у себя в лексическом окружении?

рони 04.08.2019 12:47

Devero97,
makeCounter() -- это новая функция,копия с образца и count новый, и эта новая функция будет менять только "свой" count.

Devero97 04.08.2019 12:59

рони,
На какой вопрос вы даете ответ?

рони 04.08.2019 13:06

Цитата:

Сообщение от Devero97
она эту переменную как-то изменяет у себя в лексическом окружении?

да

Devero97 04.08.2019 13:12

рони,
Каким образом это происходит?
Как функция plus() при вызове сохраняет в себе измененное значение и последующем вызове plus() это значение снова изменяется?

Про то, как при повторном вызове makeCounter2() переменная заново инициализируется я понял, мне бы понять каким образом в функции plus() сохраняется эта переменная.


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