Замыкания
Что такое замыкание?
Замыкание это функция у которой есть доступ к своей внешней функции по области видимости, даже после того, как внешняя функция прекратилась. Это говорит о том, что замыкание может запоминать и получать доступ к переменным, и аргументам своей внешней функции, даже после того, как та прекратит выполнение.
Что такое лексическая область видимости?
Лексическая область видимости это статическая область в JavaScript, имеющая прямое отношение к доступу к переменным, функциям и объектам, основываясь на их расположении в коде.

Тут функция inner имеет доступ к переменным в своей области видимости, в области видимости функции outer и глобальной области видимости. Функция outer имеет доступ к переменным, объявленным в собственной области видимости и глобальной области видимости.
Цепочка области видимости будет такой:

Обратите внимание, что функция inner окружена лексической областью видимости функции outer, которая, в свою очередь, окружена глобальной областью видимости. Поэтому функция innerимеет доступ к переменным, определенным в функции outer и глобальной области видимости.
Пример 1:

В этом примере мы вызываем функцию person, которая возвращает внутреннюю функцию displayName и сохраняет эту внутреннюю функцию в переменную peter. Когда мы вызываем функцию peter(которая на самом деле ссылается к функции displayName), имя “Peter” выводится в консоль.
Но у нас же нет никакой переменной с именем name в displayName, так что эта функция как-то может получить доступ к переменной своей внешней функции person, даже после того, как та функция выполнится. Так что, функция displayName это ни что иное как замыкание.
Пример 2:

И снова, мы храним анонимную внутреннюю функцию, возвращенную функцией getCounter в переменной count. Так как функция сount теперь замыкание, она может получать доступ к переменной counter в функции getCounter, даже после того, как та завершится.
Но обратите внимание, что значение counter не сбрасывается до 0 при каждом вызове count, как вроде бы она должна делать.
Так происходит, потому что при каждом вызове count(), создаётся новая область видимости, но есть только одна область видимости, созданная для getCounter, так как переменная counter объявлена в области видимости getCounter(), она увеличится при каждом вызове функции count, вместо того, чтобы сброситься до 0.
Как работают замыкания?
До этого момента мы обсуждали то, чем являются замыкания и их практические примеры. Сейчас давайте поймём то, как замыкания на самом деле работают в JavaScript.
Чтобы реально это понять, нам надо разобраться в двумя самыми важными концепциями в JavaScript, а именно,
1) Контекст выполнения
2) Лексическое окружение.
Контекст выполнения
Это абстрактная среда, в которой JavaScript код оценивается и выполняется. Когда выполняется “глобальный” код, он выполняется внутри глобального контекста выполнения, а код функции выполняется внутри контекста выполнения функции.
Тут может быть только один запущенный контекст выполнения (JavaScript это однопоточный язык), который управляется стеком запросов.
Стек выполнения это стек с принципом LIFO (Последний вошёл, первый вышел), в котором элементы могут быть добавлены или удалены только сверху стека.
Запущенный контекст выполнения будет всегда сверху стека и когда запущенная функция завершится, её контекст выполнения выкинется из стека, запустив контекст выполнения, который стоит ниже в очереди.

1) Во время выполнения этого кода, движок JavaScript создаёт глобальный контекст вызова, для того, чтобы выполнить глобальный код и когда он доходит до вызова функции first(), он создаёт новый контекст выполнения для этой функции и ставит её на вершину стека вызовов.
Так что он будет выглядеть таким образом для кода выше:

2) Когда функция first() завершится, её стек выполнения удалится и начнется выполнение кода ниже. Так что оставшийся код в глобальной области видимости будет выполнен.
Лексическое окружение
Каждый раз, когда движок JavaScript создаёт контекст выполнения, чтобы выполнить функцию или глобальный код, он также создаёт новое лексическое окружение, чтобы хранить переменную определенную в этой функции во время её выполнения.
Лексическое окружение это структура данных, которая хранит информацию по идентификаторам переменных. Тут идентификатор обозначает имя переменных/функций, а переменная настоящий объект[включая тип функции] или примитивное значение.
У лексического окружения есть два компонента:
(1) запись в окружении
(2) отсылка к внешнему окружению.
1. Запись в окружении(environment record) это место хранятся объявления переменной или функции.
2. Отсылка к внешнему окружению (reference to the outer environment) означает то, что у него есть доступ к внешнему (родительскому) лексическому окружению. Этот компонент самый важный для понимания того, как работают замыкания.


Когда движок JavaScript создаёт глобальный контекст выполнения, чтобы выполнить глобальный код, он также создаёт новое лексическое окружение, чтобы хранить переменные и функции, определенные в глобальной области видимости. Так что лексическое окружение для глобальной области видимости будет выглядеть примерно так:

Тут лексическое окружение выставлено на null, потому что нет внешнего лексического окружения для глобальной области видимости.
Когда движок создаёт контекст выполнения для функции first(), он также создаёт лексическое окружение для хранения переменных, объявленных в этой функции во время выполнения. Таким образом, лексическое окружение функции будет выглядеть вот так

Обратите внимание — когда функция выполняется, её контекст выполнения удаляется из стека, но её лексическое окружение может или не может быть удалено из памяти, в зависимости от того, ссылается ли на это лексическое окружение другое лексическое окружение.

Когда выполняется функция person, JavaScript создаёт новый контекст выполнения и лексическое окружение для функции. После того, как эта функция завершится, она вернёт displayName функцию и назначится на переменную peter.
Таким образом, её лексическое окружение будет выглядеть так:

При выполнении функции peter (которая на самом деле является отсылкой к функции displayName), JavaScript создаёт новый контекст выполнения и лексическое окружение для этой функции.
Так что его лексическое окружение будет выглядеть таким образом:

В функции displayName нет переменной, её запись окружения будет пуста. Во время выполнения этой функции, JavaScript будет пытаться найти переменную name в лексическом окружении функции.
Так как там нет переменных в лексическом окружении функции displayName, она будет искать во внешнем лексическом окружении, то есть, лексическом окружении функции person, которое до сих пор в памяти. JavaScript найдёт эту переменную и name выводится в консоль.

Снова, лексическое окружение для функции getCounter будет выглядеть таким образом:

Эта функция возвращает анонимную функцию и назначает её на переменную count.
Когда функция count выполняется, её лексическое окружение будет выглядеть таким образом:

Когда функция count вызывается, JavaScript начнет поиск в лексическом окружении этой функции на наличие переменной counter. Снова, если ее запись окружения пуста, то движок пойдёт искать во внешнем лексическом окружении функции.
Движок находит переменную, выводит её в консоль и увеличивает переменную counter в лексическом окружении getCounter функции.
Таким образом, лексическое окружение для функции getCounterпосле первого вызова функции count будет выглядеть таким образом:

На каждом вызове функции count, JavaScript создаёт новое лексическое окружение для функции count, увеличивает переменную count и обновляет лексическое окружения функции getCounter, чтобы соответствовать изменениям.
TODO:
Написать функцию хранилища с 4 методами:
add, pop, get, isContain.
Метод add имеет два параметра: элемент, позиция(begin, end)
Метод pop имеет один параметр: позиция(begin, end)
У методов add, pop позиция по дефолту конец.
isContain(elem)
get() возвращает структуру
Замыкания JavaScript
By pavel_efimov
Замыкания JavaScript
- 443