Просмотр полной версии : Замыкание. В учебнике не разбирают один момент.
Devero97
03.08.2019, 19:51
Здравствуйте. Объясните пожалуйста, почему в теме про замыкание не объясняется то, что функция присваивается переменной?
https://learn.javascript.ru/article/closure/lexenv-nested-makecounter-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
Хм.. А каким образом сохраняется контекст в переменную?
Как происходит запоминание увеличенного значения?
Давайте немного перепишем пример:
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
Давайте немного перепишем пример:
function makeCounter2(){
let count = 0;
function plus(){
console.log(count++);
}
plus();
plus();
plus();
}
makeCounter2();
Так понятней как происходит запоминание увеличенного значения?
Замыкание делает абсолютно то же самое, только ссылка на внутреннюю функцию plus() попадает за пределы ф-ии makeCounter2() и может вызываться оттуда.
Но новый вызов makeCounter2() выведет три числа опять с нуля, т.к. первой строкой создаётся новая переменная count со значением ноль. Конструкция makeCounter()() работает ровно так же.
Вот тут и происходит недопонимание. Даже в том примере, который вы переписали, где происходит вызов функции plus() внутри функции makeCounter(). Функция plus() после вызова перезаписывает значение переменной или она только запоминает как-то у себя это значение?
Я понимаю, что она по лексическому окружению не видит локальной переменной и по ссылке идет в другое лексическое окружение, и при обнаружении нужной переменной она ее берет и изменяет в другом лексическом окружении или она эту переменную как-то изменяет у себя в лексическом окружении?
Devero97,
makeCounter() -- это новая функция,копия с образца и count новый, и эта новая функция будет менять только "свой" count.
Devero97
04.08.2019, 12:59
рони,
На какой вопрос вы даете ответ?
она эту переменную как-то изменяет у себя в лексическом окружении?
да
Devero97
04.08.2019, 13:12
рони,
Каким образом это происходит?
Как функция plus() при вызове сохраняет в себе измененное значение и последующем вызове plus() это значение снова изменяется?
Про то, как при повторном вызове makeCounter2() переменная заново инициализируется я понял, мне бы понять каким образом в функции plus() сохраняется эта переменная.
Devero97,
как это работет понятно ?
let count = 0;
function plus(){
console.log(count++);
}
это яблоко!!!
а ниже это сад по производству таких яблок
function makeCounter(){
let count = 0;
return function(){
console.log(count++);
}
}
а это создание нового яблока
makeCounter();
чтобы не писать каждый раз
let count = 0;
function plus(){
console.log(count++);
}
1. образец функции.
2. фабрика функций.
3. экземпляр функции.
Хм... до прочтения этой темы я даже не слышал такого слова "замыкание"... Думаю, что название совершенно не передает смысл этой технологии, если я вообще правильно ее понял ))
Попытаюсь Devero97, объяснить "замыкание", насколько его понял я.
Допустим, мы пишем функцию, которая должна возвращать некие данные, которые зависят от своих предыдущих значений. В Вашем примере это счетчик. Понятно, что если значение счетчика разместить внутри функции, т.е. сделать его локальным, то счетчик просуществует только пока функция выполняется, после чего его значение будет потеряно. Тогда логично поместить счетчик в глобальной переменной (я так и делал) и тогда его значение не будет потеряно, но как только код станет большим, начнутся проблемы - лишняя глобальная переменная требует дополнительного описания, что бы самому вспомнить, для чего она и откуда вызывается. Еще хуже, когда Вы будете переносить "отлаженную" функцию в другой код, Вы запросто забудете перетащить с собой и глобальную переменную. Вдобавок мы можем сделать еще одну функцию, которая тоже использует глобальную переменную, да и с тем же именем... есть и другие проблемы.
Теперь про хитрость: В примере получена глобальная функция, которая на самом деле является дочерней для другой функции. Будучи дочерней функцией, она должна иметь доступ ко всем данным родительской функции, как они были на момент описания дочерней функции. Поэтому все данные удерживаются в памяти (надеюсь, только используемые), как буд-то родительская фукция все еще выполняется. Но эти данные родительской функции уже не нужны, т.к. ее выполнение уже закончилось, поэтому дочерняя функция получает их независимую копию, которые хранятся как глобальные, но доступны только этому экземпляру дочерней функции, как локальные, и не перескаются с данными других копий этой же дочерней функции.
MC-XOBAHCK
04.08.2019, 15:51
рони,
вот тут нет левого сайтбара с навигацией по статье: https://learn.javascript.ru/attributes-and-custom-properties
и в содержании учебника этой статьи нет. Зато в гугле есть.
Devero97
04.08.2019, 17:07
NiOl,
рони, уже объяснил то, что эти внешние переменные, доступные по ссылке на лексическое окружение внешней функции сохраняются в лексическом окружении вызываемой функции и таким образом происходит запоминание и изменение числа. Вроде бы нет вопросов больше.
MC-XOBAHCK,
учебник редактируют, ссылка на новую редакцию темы: атрибуты, тут
https://learn.javascript.ru/dom-attributes-and-properties
Если что-то не так - пожалуйста, поправьте в PR на GitHub (ссылка на редактирование слева в сайдбаре статьи). Спасибо!
vBulletin® v3.6.7, Copyright ©2000-2025, Jelsoft Enterprises Ltd. Перевод: zCarot