Зачем нужны замыкания? (JavaScript) [дубликат]
Коллеги! Теоретически я как бы понял сущность замыкания: это высшая функция, возвращающая другую, «дочернюю» функцию, при этом должны быть переменные, замкнутые в области видимости «между» высшей и дочерней. Дочерняя функция вызывается в глобальной области и обрабатывает эти переменные и свои аргументы. Как бы создается один пакет из двух «набираемых» в двух независимых вызовах кортежей данных. ВОПРОС: а какова необходимость в замыкании, конкретно, какой род проблем оно решает, какой тип задач, для реализации какого алгоритма нужен?
Отслеживать
Владимир Симиненко
задан 20 дек 2021 в 11:05
Владимир Симиненко Владимир Симиненко
1 2 2 бронзовых знака
А как пользоваться ооп не имея его?
20 дек 2021 в 11:07
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Думаю будет легче понять что это заглянув в историю. Сегодня конечно имея дополнительные конструкции языка это может казаться бесмысленным.
function makeFunc() < var name = "Mozilla"; function displayName() < alert(name); >return displayName; >; var myFunc = makeFunc(); myFunc();
Замыкания на практике.
Замыкания полезны тем, что позволяют связать данные (лексическое окружение) с функцией, которая работает с этими данными. Очевидна параллель с объектно-ориентированным программированием, где объекты позволяют нам связать некоторые данные (свойства объекта) с одним или несколькими методами.
Следовательно, замыкания можно использовать везде, где вы обычно использовали объект с одним единственным методом.
Такие ситуации повсеместно встречаются в web-разработке. Большое количество front-end кода, который мы пишем на JavaScript, основано на обработке событий. Мы описываем какое-то поведение, а потом связываем его с событием, которое создаётся пользователем (например, клик мышкой или нажатие клавиши). При этом наш код обычно привязывается к событию в виде обратного/ответного вызова (callback): callback функция — функция выполняемая в ответ на возникновение события.
Языки вроде Java позволяют нам объявлять частные (private) методы . Это значит, что они могут быть вызваны только методами того же класса, в котором объявлены.
JavaScript не имеет встроенной возможности сделать такое, но это можно эмулировать с помощью замыкания. Частные методы полезны не только тем, что ограничивают доступ к коду, это также мощное средство глобальной организации пространства имён, позволяющее не засорять публичный интерфейс вашего кода внутренними методами классов
что такое замыкание js
«Замыкание» — это способность функции запоминать переменные, которые были определены внутри родительской функции, даже после того, как родительская функция была выполнена.
function createCounter() // переменная, которую нужно запомнить let count = 0; function counter() // увеличиваем нашу запомненную переменную count++; console.log(count); > // возвращаем функцию return counter; > // создаем новую функцию (с замыканием) const incrementCounter = createCounter(); incrementCounter(); // 1 incrementCounter(); // 2 incrementCounter(); // 3
В этом примере мы создали функцию createCounter() , которая создает другую функцию counter() . Внутри функции мы создали переменную count , которая была определена внутри родительской функции. Функция counter() возвращает значение count , увеличивая его на 1 . Когда мы вызываем createCounter() , она возвращает функцию counter() , которая имеет доступ к count благодаря замыканию. Каждый раз, когда мы вызываем incrementCounter() ,
Если мы создадим новый счетчик с помощью функции createCounter() , то отсчет для него начнется заново.
// Создали еще одну новую функци-счетчик const newIncrementCounter = createCounter(); newIncrementCounter(); // 1 newIncrementCounter(); // 2 newIncrementCounter(); // 3
Что такое замыкание в программировании
Сегодня разбираем полезное понятие из мира программирования — замыкание. Это нужно тем, кто хочет серьёзно заниматься разработкой и говорить со старшими товарищами на одном языке.
Для начала потребуется два термина: область видимости и стек. Если нужно вспомнить, раскрывайте:
Что такое область видимости
Область видимости определяет, к каким переменным функция, команда или другая переменная может получить доступ, а к каким нет. Проще говоря, что каждая функция «видит» — видит ли она те переменные и объекты, которые созданы за её пределами? Видит ли она то, что вложено в функции, которые запускаются внутри неё?
Обычно так: если переменная объявлена в самой программе, к ней можно получить доступ из вложенной функции. А если объявить переменную внутри функции, то не обратиться к ней извне не получится.
Что такое стек
Стек — это как список задач при выполнении программы. Сначала компьютер исполняет один код, внутри которого появляется какая-то функция — это как будто отдельная программа. Компьютер откладывает текущую программу, делает себе в стеке пометку «вернуться сюда, когда доделаю новую программу» и исполняет эту новую функцию. Исполнил — смотрит в стек, куда вернуться. Возвращается туда.
Про стек нужно думать, потому что он не безразмерный. Нельзя бесконечно вкладывать функции друг в друга.
Что такое замыкание
Замыкание в программировании — это когда одна функция возвращает как результат своей работы не переменную, а другую функцию. При этом хитрость в том, что внутренняя функция имеет доступ к переменным из внешней функции и может с ними работать в любой момент.
Чаще всего это используют, чтобы сделать переменную, которая на самом деле работает как функция.
Звучит сложно, объясним на примерах.

Пример замыкания в коде
Сделаем функцию, которая вернёт нам фразу «Привет, …» вместе с именем, которое мы в него отправим. Но сделаем это через замыкание — чтобы посмотреть, как именно всё устроено.
Обратите внимание на две переменные в конце: mike и maxim. По сути, эти переменные — ссылки на вызов результата функции hello(), но с конкретным параметром.
// внешняя функция function hello(name) < // внутренняя функция-замыкание // возвращаем её как результат работы внешней return function() < // внутренняя функция выводит сообщение на экран console.log("Привет, " + name); >> // создаём новые переменные, используя замыкание mike = hello("Миша"); maxim = hello("Максим") // запускаем внутреннюю функцию с нашими параметрами, просто указав имена переменной mike(); maxim();

Ещё один пример, посложнее
Только что мы сделали просто — привязали сообщение к переменной без возможности на него повлиять. Но можно создавать такие переменные, у которых могут быть свои параметры — и в них уже передавать любые значения.
Для примера сделаем замыкание и заведём две переменные — каждая будет выдавать сообщение со своим началом фразы, а продолжение будем передавать им в виде параметра:
// внешняя функция, у которой есть свой параметры function greeting(hi) < // внутренняя функция-замыкание, тоже со своим параметром // возвращаем её как результат работы внешней return function(name) < // внутренняя функция выводит сообщение на экран console.log(hi + ", " + name); >> // создаём новые переменные, используя замыкание morning = greeting("Доброе утро"); thnx = greeting("Спасибо за комментарий") // запускаем внутреннюю функцию с нашими параметрами, указав параметры вызова morning("коллеги"); thnx("Павел");

Причём здесь область видимости
Из последнего примера видно, что объявление переменных происходит так:
- переменной присваивается результат выполнения внешней функции;
- этот результат зависит от параметра, который мы ей передали;
- потом, при обращении к переменной, происходит обработка только второго параметра, а первый хранится где-то в памяти и подставляется в нужный момент.
Но как такое может быть, когда внутренняя функция использует переменную из области видимости внешней функции, которой уже нет?
Всё дело в том, что при замыкании создаётся как бы виртуальное окружение, в котором и хранится значение из внешней функции. А вот как это работает с точки зрения области видимости:

Зачем нужны замыкания
На замыканиях строится около половины алгоритмов в функциональном программировании. А ещё на них можно построить много разного:
- изолировать логику выполнения фрагментов кода, если это не позволяют сделать встроенные возможности языка (как в JavaScript);
- лучше структурировать код, особенно при организации функций, которые отличаются только несколькими элементами;
- реализовать инкапсуляцию в тех языках, где её нет.
Что дальше
Следующий шаг — попробовать замыкания в деле. Напишем небольшой код и проверим, как работают замыкания и для чего они могут пригодиться.
Понимаем замыкания в JavaScript. Раз и навсегда
Тема измученная, старая, но всё равно в ней часто делаются ошибки. На самом деле замыкания могут показаться запутанными с первого взгляда, но по факту тут нет ничего сложного. В этой статье собраны переводы двух статей. Первая это теория, которая показывает то, что находится под капотом, а вторая это довольно интересные примеры для закрепления материала.
Мой Твиттер — там много из мира фронтенда, да и вообще поговорим. Подписывайтесь, будет интересно: ) ✈️
Замыкания это фундаментальная концепция JavaScript, которую должен понимать каждый разработчик пишущий на нем. Да, она сбивает с толку многих новичков в JS.
Имея четкое понимание замыканий, вы будете писать код лучше, куда эффективнее и чище. Что в итоге, поможет вам в продвижении своих профессиональных навыков.
В этой статье я попытаюсь объяснить внутреннюю структуру замыканий и то, как они на самом деле работают в JavaScript.
Давайте уже начнём.
Что такое замыкание?
Замыкание это функция у которой есть доступ к своей внешней функции по области видимости, даже после того, как внешняя функция прекратилась. Это говорит о том, что замыкание может запоминать и получать доступ к переменным, и аргументам своей внешней функции, даже после того, как та прекратит выполнение.
Перед тем как мы углубимся в замыкания, давайте сначала поймем лексическую область видимости.
Что такое лексическая область видимости?
Лексическая область видимости это статическая область в JavaScript, имеющая прямое отношение к доступу к переменным, функциям и объектам, основываясь на их расположении в коде. Вот пример:
let a = 'global';function outer() let b = 'outer';function inner() let c = 'inner'
console.log(c); // выдаст 'inner'
console.log(b); // выдаст 'outer'
console.log(a); // выдаст 'global'
>
console.log(a); // выдаст 'global'
console.log(b); // выдаст 'outer'
inner();
>
outer();
console.log(a); // выдаст 'global'
Тут функция inner имеет доступ к переменным в своей области видимости, в области видимости функции outer и глобальной области видимости. Функция outer имеет доступ к переменным, объявленным в собственной области видимости и глобальной области видимости.
В общем, цепочка области видимости выше будет такой:
Global outer inner
>
>
Обратите внимание, что функция inner окружена лексической областью видимости функции outer , которая, в свою очередь, окружена глобальной областью видимости. Поэтому функция inner имеет доступ к переменным, определенным в функции outer и глобальной области видимости.
Практические примеры замыкания
Давайте взглянем на практические примеры замыканий, перед тем как углубляться в то, как они работают.
Пример 1:
function person() let name = 'Peter';
return function displayName() console.log(name);
>;
>let peter = person();
peter(); // выведет 'Peter'
В этом примере мы вызываем функцию person , которая возвращает внутреннюю функцию displayName и сохраняет эту внутреннюю функцию в переменную peter . Когда мы вызываем функцию peter (которая на самом деле ссылается к функции displayName ), имя “Peter” выводится в консоль.
Но у нас же нет никакой переменной с именем name в displayName , так что эта функция как-то может получить доступ к переменной своей внешней функции person , даже после того, как та функция выполнится. Так что, функция displayName это ни что иное как замыкание.
Пример 2:
function getCounter() let counter = 0;
return function() return counter++;
>
>let count = getCounter();console.log(count()); // 0
console.log(count()); // 1
console.log(count()); // 2
И снова, мы храним анонимную внутреннюю функцию, возвращенную функцией getCounter в переменной count . Так как функция сount теперь замыкание, она может получать доступ к переменной counter в функции getCounter , даже после того, как та завершится.
Но обратите внимание, что значение counter не сбрасывается до 0 при каждом вызове count , как вроде бы она должна делать.
Так происходит, потому что при каждом вызове count() , создаётся новая область видимости, но есть только одна область видимости, созданная для getCounter , так как переменная counter объявлена в области видимости getCounter() , она увеличится при каждом вызове функции count , вместо того, чтобы сброситься до 0 .
Как работают замыкания?
До этого момента мы обсуждали то, чем являются замыкания и их практические примеры. Сейчас давайте поймём то, как замыкания на самом деле работают в JavaScript.
Чтобы реально это понять, нам надо разобраться в двумя самыми важными концепциями в JavaScript, а именно, 1) Контекст выполнения и 2) Лексическое окружение.
Контекст выполнения
Это абстрактная среда, в которой JavaScript код оценивается и выполняется. Когда выполняется “глобальный” код, он выполняется внутри глобального контекста выполнения, а код функции выполняется внутри контекста выполнения функции.
Тут может быть только один запущенный контекст выполнения (JavaScript это однопоточный язык), который управляется стеком запросов.
Стек выполнения это стек с принципом LIFO (Последний вошёл, первый вышел), в котором элементы могут быть добавлены или удалены только сверху стека.
Запущенный контекст выполнения будет всегда сверху стека и когда запущенная функция завершится, её контекст выполнения выкинется из стека, запустив контекст выполнения, который стоит ниже в очереди.
Давайте посмотрим на пример кода, чтобы лучше понять контекст выполнения и стек:
Во время выполнения этого кода, движок JavaScript создаёт глобальный контекст вызова, для того, чтобы выполнить глобальный код и когда он доходит до вызова функции first() , он создаёт новый контекст выполнения для этой функции и ставит её на вершину стека вызовов.
Так что он будет выглядеть таким образом для кода выше:
Когда функция first() завершится, её стек выполнения удалится и начнется выполнение кода ниже. Так что оставшийся код в глобальной области видимости будет выполнен.
Лексическое окружение
Каждый раз, когда движок JavaScript создаёт контекст выполнения, чтобы выполнить функцию или глобальный код, он также создаёт новое лексическое окружение, чтобы хранить переменную определенную в этой функции во время её выполнения.
Лексическое окружение это структура данных, которая хранит информацию по идентификаторам переменных. Тут идентификатор обозначает имя переменных/функций, а переменная настоящий объект[включая тип функции] или примитивное значение.
У лексического окружения есть два компонента: (1) запись в окружении и (2) отсылка к внешнему окружению.
- Запись в окружении(environment record) это место хранятся объявления переменной или функции.
2. Отсылка к внешнему окружению (reference to the outer environment) означает то, что у него есть доступ к внешнему (родительскому) лексическому окружению. Этот компонент самый важный для понимания того, как работают замыкания.
Лексическое окружение на самом деле выглядит так:
lexicalEnvironment = environmentRecord: : ,
:
>
outer: < Reference to the parent lexical environment>
>
Теперь снова, давайте посмотрим на пример кода выше:
let a = 'Hello World!';function first() let b = 25;
console.log('Inside first function');
>
first();
console.log('Inside global execution context');
Когда движок JavaScript создаёт глобальный контекст выполнения, чтобы выполнить глобальный код, он также создаёт новое лексическое окружение, чтобы хранить переменные и функции, определенные в глобальной области видимости. Так что лексическое окружение для глобальной области видимости будет выглядеть примерно так:
globalLexicalEnvironment = environmentRecord: a : 'Hello World!',
first : < reference to function object >
>
outer: null
>
Тут лексическое окружение выставлено на null , потому что нет внешнего лексического окружения для глобальной области видимости.
Когда движок создаёт контекст выполнения для функции first() , он также создаёт лексическое окружение для хранения переменных, объявленных в этой функции во время выполнения. Таким образом, лексическое окружение функции будет выглядеть вот так:
functionLexicalEnvironment = environmentRecord: b : 25,
>
outer:
>
Внешнее лексическое окружение функции указывается в глобальном лексическом окружении, потому что функция окружена глобальной областью видимости в исходном коде.
Обратите внимание — когда функция выполняется, её контекст выполнения удаляется из стека, но её лексическое окружение может или не может быть удалено из памяти, в зависимости от того, ссылается ли на это лексическое окружение другое лексическое окружение.
А теперь детально о примерах замыканий
Теперь, когда мы поняли контекст выполнения и лексическое окружение, давайте вернёмся к замыканиям.
Пример 1:
function person() let name = 'Peter';
return function displayName() console.log(name);
>;
>let peter = person();
peter(); // prints 'Peter'
Когда выполняется функция person , JavaScript создаёт новый контекст выполнения и лексическое окружение для функции. После того, как эта функция завершится, она вернёт displayName функцию и назначится на переменную peter .
Таким образом, её лексическое окружение будет выглядеть так:
personLexicalEnvironment = environmentRecord: name : 'Peter',
displayName: < displayName function reference>
>
outer:
>
Когда функция person завершится, её контекст выполнения выкинется из стека. Но её лексическое окружение всё ещё останется в памяти, так как на него ссылается лексическое окружение его внутренней функции displayName . Таким образом, её переменные всё ещё будут доступны в памяти.
При выполнении функции peter (которая на самом деле является отсылкой к функции displayName ), JavaScript создаёт новый контекст выполнения и лексическое окружение для этой функции.
Так что его лексическое окружение будет выглядеть таким образом:
displayNameLexicalEnvironment = environmentRecord:
>
outer:
>
В функции displayName нет переменной, её запись окружения будет пуста. Во время выполнения этой функции, JavaScript будет пытаться найти переменную name в лексическом окружении функции.
Так как там нет переменных в лексическом окружении функции displayName, она будет искать во внешнем лексическом окружении, то есть, лексическом окружении функции person , которое до сих пор в памяти. JavaScript найдёт эту переменную и name выводится в консоль.
Пример 2:
function getCounter() let counter = 0;
return function() return counter++;
>
>let count = getCounter();console.log(count()); // 0
console.log(count()); // 1
console.log(count()); // 2
Снова, лексическое окружение для функции getCounter будет выглядеть таким образом:
getCounterLexicalEnvironment = environmentRecord: counter: 0,
: < reference to function>
>
outer:
>
Эта функция возвращает анонимную функцию и назначает её на переменную count .
Когда функция count выполняется, её лексическое окружение будет выглядеть таким образом:
countLexicalEnvironment = environmentRecord:
>
outer:
>
Когда функция count вызывается, JavaScript начнет поиск в лексическом окружении этой функции на наличие переменной counter . Снова, если ее запись окружения пуста, то движок пойдёт искать во внешнем лексическом окружении функции.
Движок находит переменную, выводит её в консоль и увеличивает переменную counter в лексическом окружении getCounter функции.
Таким образом, лексическое окружение для функции getCounter после первого вызова функции count будет выглядеть таким образом:
getCounterLexicalEnvironment = environmentRecord: counter: 1,
: < reference to function>
>
outer:
>
На каждом вызове функции count , JavaScript создаёт новое лексическое окружение для функции count, увеличивает переменную count и обновляет лексическое окружения функции getCounter , чтобы соответствовать изменениям.
Закрепляем понимание замыканий в JavaScript на примерах
Замыкания это такая вещь в JavaScript, что сколько бы вы про неё не читали, всё равно полностью не поймете. Как и с многим в программировании, вам нужно покрутить-повертеть какие-нибудь примеры, чтобы полностью принять и понять нужную концепцию.
Теперь давайте посмотрим на некоторые примеры того, как же используются замыкания и пройдёмся по процессу создания вашего приложения, где мы воспользуемся преимуществами вышеупомянутых замыканий.
Снова, что такое замыкание?
Мне очень нравится определение из Secrets of the JavaScript:
Замыкание это способ получения доступа и управления внешними переменными из функции.
Мне нравится рассматривать замыкания как функцию языка программирования, которая позволяет нам делать крутые вещи, такие как:
Объектно-ориентированное программирование в JavaScript: