Замыкание — фундаментальная концепция JavaScript, о которой должен знать каждый программист!

ПОЧЕМУ??

Это дает нашим функциям постоянную память. Давайте узнаем, как это сделать!

Сказав это, вполне естественно, что Интернет перегружен объяснениями, которые так или иначе бесценны.

Лично мне легче понять концепцию, когда я получаю визуальное объяснение того, что это за инструмент и как он делает мой код более эффективным.

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

Что такое закрытие?

Как определено в MDN:

Замыкание — это комбинация функции, связанной вместе (заключенной) со ссылками на ее окружающее состояние (лексическое окружение). Другими словами, замыкание дает вам доступ к области действия внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз при создании функции, во время создания функции.

Функции в JavaScript формируют замыкания. Замыкание — это комбинация функции в лексическом окружении, в котором эта функция была объявлена. Эта среда состоит из любых локальных переменных, которые находились в области видимости во время создания замыкания.

Примечание. Весь файл JavaScript представляет собой одну область действия, а функция — другую область действия.

Давайте посмотрим на код, например:

Когда мы запускаем функцию с вводом 3, ввод 3 абсолютно забывается при следующем запуске функции с вводом 10. Итак, когда функция запускается внутри нее, мы получаем хранилище данных. В следующий раз, когда функция запускается с другим входом, мы получаем совершенно новые данные, и, следовательно, между этими двумя запущенными функциями нет никакой связи.

Обычно, когда наши функции запускаются, мы создаем внутри них динамическое хранилище данных, в котором есть локальная память для выполнения этой функции. Итак, когда мы запускаем функцию со входом 3 и получаем 6, 3 (вход) удаляется, и единственное, что сохраняется, — это возвращаемый результат 6.

Как вернуть функцию из другой функции?

Когда JavaScript проходит код построчно, он сохраняет данные в памяти следующим образом: когда он видит ключевое слово function, он сохраняет эту функцию или код на потом. По существу, функция состоит из двух частей: имя функции и полностью отдельного от нее хранимого кода функции.

В первой строке объявляется функция, и JavaScript сохраняет ее в глобальной памяти. Во второй строке мы создаем const diffLabel для глобальной памяти, и ей будет присвоено определение функции createFunction.

Мы сохранили функцию и ее метку в одном блоке, и у нас есть новая метка для того же кода функции во второй строке кода. У нас есть две метки для одного и того же кода функции.

В третьей строке мы ничего не сохраняем в generatedFunction до тех пор, пока не запустим createFunction и не посмотрим, что будет на выходе, и этот результат будет сохранен в generatedFunction.

Когда мы запускаем функцию, мы создаем контекст выполнения, и когда мы выполняем функцию, например, createFunction, ей нужны две вещи: мини-память всего, что создается, а затем она собирается пройтись по коду строка за строкой, это называется поток, и если ему нужно что-то сохранить, это будет сохранено в локальной памяти.

Следовательно, первое, что сохраняется внутри createFunction, — это метка multiplyBy2, которая назначается определению функции multiplyBy2. Далее мы собираемся вернуть multiplyBy2, и он будет сохранен в generatedFunction. Причина в том, что когда JavaScript видит ключевое слово return, он ищет метку, берет определение функции и возвращает его в метку generatedFunction. Ранее он был помечен как multiplyBy2, и JavaScript использовал его для определения определения. И теперь он выходит из этого контекста выполнения и переходит к следующей строке.

В следующей строке мы объявляем метку result, которая равна выходу вызванной функции generatedFunction. Другими словами, запустите createFunction и посмотрите, что будет возвращено. Он возвращает полное определение функции multiplyBy2, возвращает его и сохраняет в generatedFunction. Таким образом, generatedFunction не имеет ничего общего с createFunction после этой строки.

Особенность JavaScript заключается в том, что когда мы объявили метку generatedFunction, мы можем поместить в нее функцию, а затем запустить эту функцию по метке, поставив () круглые скобки в конце. Это именно то, что мы делаем в следующей строке, мы объявляем result и помещаем () в конец generatedFunction, и JavaScript переходит к просмотру generatedFunction и находит функцию, ранее известную как multiBy2. Затем мы создаем то, что известно как контекст выполнения, который представляет собой просто мини-приложение для запуска функции, и мы используем код multiplyBy2 для его запуска. Именно так мы запустили createFunction и сохранили результат в generatedFunction. Таким образом, вывод здесь заключается в том, что ничего не сохраняется в generatedFunction, пока мы не запустим createFunction, и то, что будет возвращено, будет сохранено в generatedFunction.

Вызов функции вне вызова функции, в которой она была определена-

Примечание. Место, где мы определяем наши функции, определяет, к каким переменным наши функции имеют доступ при вызове функции.

Приведенный выше код работает аналогично предыдущему примеру, поэтому в первой строке метка outer сохраняется в глобальной памяти вместе с определением функции. В следующей строке Javascript создает новый контекст выполнения с локальной памятью для запуска outer, он установит там переменную counter и присвоит ей значение 0. Затем он сохраняет определение функции incrementCounter и возвращает определение incrementCounter. Вывод outer будет храниться в глобальной метке myNewFunction. Таким образом, функция, ранее известная как incrementCounter, теперь называется myNewFunction. И он выходит из этого контекста выполнения.

Теперь, когда Javascript переходит к следующей строке, myNewFunction(), он создает здесь новый контекст выполнения и собирается запустить определение функции incrementCounter в глобальной памяти, а не внутри внешнего. Сначала он ищет определение myNewFunction в локальной памяти, а затем в глобальной.

Итак, мы запустили outer и вернули incrementCounter, и теперь incrementCounter запускается под своим новым именем myNewFunction, которое имеет counter++ внутри, но JavaScript не может вернуться во внешний, так как внешняя память уменьшилась, остается вопрос, как он все еще будет иметь доступ к функциям outer.

Вот что происходит, когда мы возвращаем incrementCounter в myNewFunction, мы передаем больше, чем просто определение функции, мы передаем всю окружающую локальную память, которая была подключена к функции. Таким образом, когда функция появилась, она взяла данные вместе с ними в рюкзак, как некоторые называют это, и сохранила их в новой глобальной метке myNewFunction. . Итак, теперь, когда он достигает следующей строки myNewFunction(), он создает другой контекст выполнения, ничего не находит в локальной памяти, поэтому он просматривает данные, прикрепленные к нему определением функции. . Другими словами, он заглядывает в рюкзак и находит counter, который равен 1, и теперь увеличивает его до 2. По сути, у нас есть доступ к постоянным данным, прикрепленным к функции, в данном случае myNewFunction. . Один важный вывод заключается в том, что этот пакет данных не находится в глобальной памяти и доступен только при запуске функции, к которой прикреплены данные. Таким образом, нам больше не нужно хранить данные в глобальной памяти, чтобы они были постоянными. Нам не пришлось бы засорять глобальную память именами или метками, поэтому вместо данных, которые мы хотим сохранить, мы помещаем их в рюкзак функции.

Примечание. Счетчик не находится в глобальной памяти. Он упакован и защищен внутри myNewFunction как метка.

** Контекст выполнения — это концепция для оценки времени выполнения кода. В любой момент времени может быть только один контекст выполнения, выполняющий код.

** Каждый контекст выполнения имеет лексическое окружениеt, которое содержит переменные и связанные с ними значения, а также имеет ссылку на его внешнее окружение.

** Набор идентификаторов, к которым имеет доступ каждая среда, называется областью. Область действия, по сути, означает, какие данные доступны нам в любой момент с помощью нашей функции, когда мы ее запускаем.

Я действительно надеюсь, что это было чем-то полезно. Эта концепция довольно нова для меня, и я был бы очень признателен за любые отзывы по этой теме.

Всем удачного кодирования!!