Изучаю JS на http://learn.javascript/, сейчас читаю подраздел «Замыкание функции изнутри» раздела «Замыкания, область видимости».
В пункте «Возврат функции» описывается пример с вложенной функцией.
Я, собственно, не понимаю, как counter при каждом запуске вспоминает переменную currentCount, учитывая написанное в том же разделе, что LexicalEnviroment при каждом завершении функции стирается из памяти, а при каждом новом запуске создается заново.
Не пойму, где хранится currentCount, если лексическое окружение стирается каждый раз?
Ответ
Такие вопросы вызывают у меня скупую слезу на бороду.
Всё просто - LexicalEnvironment действительно уничтожается после окончания работы вызова.
Но в примере работа не закончена, т. к. возвращаемая функция сохранила ссылку на родительский [makeCounter] объект переменных.
То есть пока возвращенная функция будет жива, родительский LE обязан сохранится, вдруг при выполнении нужно обратится к родительской функции за переменными (что и происходит: в возвращённой функции нет переменной currentCount)?
Сборщики мусора работают по алгоритму ссылок (Mark and sweep), в общих чертах это как-то так:
Находятся корни, т. е. то, что всегда будет существовать (в браузерах это объект window, его же нельзя удалить).
После каждого пробуждения, он [сборщик] рекурсивно обходит корни и помечает всё, до чего может дотянутся; это считается как нужное и полезное. Всё остальное считается недостижимым и память из-под них возвращается среде.
А теперь вспомним, что возвращённая функция, условно сохранённая в window, ссылается на родительский LexicalEnvironment через скрытое свойство [[Scope]]
Сборщик просто не может удалить его - window -> counter -> [[Scope]]
Это объясняет почему новый вызов makeCounter создал девственно чистый счётчик - каждый вызов функции выдаёт ей кристально новый LexicalEnvironment
Замыкания - вообще очень интересная вещь, которая может быть очень мощной, а может в конец запутать, если не знать принципов работы замыканий.
Комментариев нет:
Отправить комментарий