Эта статья переведена сblog.bit SRC.IO/Ах-новички…, автор Сухджиндер Арора, частично изменено содержание и изменено название.
Замыкания — это фундаментальная концепция, которую должен знать и освоить каждый программист JavaScript. Однако эта концепция сбивает с толку многих новичков в JavaScript.
Правильное понимание замыканий поможет вам писать более качественный, эффективный и чистый код. В свою очередь, это поможет вам стать лучшим разработчиком JavaScript.
Итак, в этой статье я попытаюсь объяснить, как работают замыкания и как они на самом деле работают в JavaScript.
Что такое закрытие
На следующем рисунке показано замыкание:
Даже после того, как закрытие является функцией возврата внешней функции, функция также может получить доступ к внешней области видимости.. Это означает, что замыкание может запоминать и получать доступ к переменным и параметрам внешней функции даже после завершения выполнения внешней функции. Следует отметить, что на иллюстрации, поскольку не используетсяage
переменная, поэтому напечатанное закрытие не имеет ее.
Прежде чем погрузиться в замыкания, давайте сначала разберемся с лексической областью видимости.
Что такое лексический объем?
в JavaScriptлексический объемилистатическая областьОтносится к доступности переменных, функций и объектов на основе их фактического расположения в исходном коде. Например:
let a = 'global';
function outer() {
let b = 'outer';
function inner() {
let c = 'inner'
console.log(c); // prints 'inner'
console.log(b); // prints 'outer'
console.log(a); // prints 'global'
}
console.log(a); // prints 'global'
console.log(b); // prints 'outer'
inner();
}
outer();
console.log(a); // prints 'global'
это здесь,inner
Функция может получить доступ в своей области видимости,outer
Область действия функции и переменные, определенные в глобальной области видимости. а такжеouter
Функция может обращаться к переменным, определенным в своей собственной области видимости и в глобальной области видимости.
Таким образом, область действия приведенного выше кода выглядит следующим образом:
Global {
outer {
inner
}
}
Уведомление,inner
функцияouter
окружен лексической областью действия функции, иouter
Лексическая область видимости функции, в свою очередь, окружена глобальной областью видимости. поэтомуinner
функция доступна вouter
Причина для переменных, определенных в функциях и глобальной области видимости.
Демонстрация замыканий
Прежде чем углубиться в то, как работают замыкания, давайте рассмотрим несколько практических примеров замыканий.
Пример 1
function person() {
let name = 'Peter';
return function displayName() {
console.log(name);
};
}
let peter = person();
peter(); // prints 'Peter'
В этом коде мы вызываемperson
функция, которая возвращает внутреннюю функциюdisplayName
и сохраните эту внутреннюю функцию вpeter
в переменной. когда мы звонимpeter
функция (фактически ссылаетсяdisplayName
функция), названиеPeter
выводится на консоль.
но мы неdisplayName
объявлено в функцииname
переменные, поэтому даже после возврата внешней функции функция может каким-то образом получить доступ к переменным своей внешней функции.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
Переменная.count
Теперь функция является замыканием, даже еслиgetCounter()
После выполнения к нему все еще можно получить доступgetCounter
внутри функцииcounter
Переменная.
Но учтите, что каждый разcount
при исполненииcounter
не сбрасывается на0
.
Это потому, что каждый раз, когда вы звонитеcount()
, создаст новую область действия для функции, но здесь толькоgetCounter
Создайте объем, и потому чтоcounter
переменная определена вgetCounter
внутри области действия функции, поэтому она будет вызываться каждый разcount
Увеличивается вместо сброса при вызове.
Как работают замыкания
До сих пор мы обсуждали, что такое замыкания, и их практические примеры. Теперь давайте поймем, что замыкания находятся вJavaScript
как это работает на самом деле.
Чтобы действительно понять, как работают замыкания в JavaScript, мы должны понимать JavaScript в двух наиболее важных концепциях, а именно: 1)контекст выполненияи 2)Лексическое окружение.
контекст выполнения
Контекст выполнения — это абстрактная среда, в которой выполняется код JavaScript. Когда выполняется глобальный код, он будет выполняться в глобальном контексте выполнения, а код функции будет выполняться в контексте выполнения функции.
Выполняется только один контекст выполнения, поскольку JavaScript — это однопоточный язык, который управляется стеком выполнения или стеком вызовов.
Стек выполнения - это стек с конструкцией LIFO (последний в первом месте), а элементы могут быть добавлены или удалены только с верхней части стека.
Текущий контекст выполнения всегда будет находиться на вершине стека, и когда текущая работающая функция завершится, ее контекст выполнения будет извлечен из стека, а указатель будет указывать на контекст выполнения под ним в стеке.
Давайте взглянем на фрагмент кода, чтобы лучше понять контекст выполнения и стек:
Когда этот код выполняется, механизм JavaScript создает глобальный контекст выполнения для выполнения глобального кода, и когда он встречаетfirst()
Когда функция вызывается, она создает новый контекст выполнения для функции и помещает ее на вершину стека выполнения.
Таким образом, стек выполнения для приведенного выше кода выглядит следующим образом:
когдаfirst()
Функция завершается, ее стек выполнения удаляется из стека, и указатель достигает контекста выполнения ниже него, глобального контекста выполнения. Поэтому оставшийся код в глобальной области будет выполняться.
Лексическое окружение
Всякий раз, когда механизм JavaScript создает контекст выполнения для выполнения этой функции или глобального кода, он создает новую лексическую среду для хранения переменных, определенных в этой функции во время ее выполнения.
Лексическое окружение — это структура данных, в которой хранятся отношения отображения между идентификаторами и переменными. Идентификаторы относятся к именам переменных или функций, а переменные относятся к реальным объектам, включая функции или примитивные значения.
Лексическое окружение состоит из трех частей: (1) записи окружения; (2)outer
Направление внешней среды; (3)this
- Записи среды — это место, где хранятся объявления переменных и функций;
-
outer
Точка представления внешней среды, которую она может получить доступ к лексической среде внешнего слоя (родительского уровня). Это ключ к пониманию принципа закрытия.
Лексическое окружение устроено следующим образом:
lexicalEnvironment = {
EnvironmentRecord: {
<identifier> : <value>,
<identifier> : <value>
}
outer: <Reference to the parent lexical environment>
ThisBinding: <Global Object>
}
Теперь проанализируйте код ниже.
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
ThisBinding: <Global Object>
}
Здесь внешнее лексическое окружение установлено вnull
Это потому, что глобальная область видимости не имеет внешней лексической среды.
когда двигательfirst()
Когда функция создает контекст выполнения, она также создает лексическую среду для хранения переменных, определенных в этой функции во время ее выполнения. Итак, лексическое окружение функции выглядит так:
functionLexicalEnvironment = {
EnvironmentRecord: {
b : 25,
}
outer: <globalLexicalEnvironment>
ThisBinding: <Global Object>
}
Внешнее лексическое окружение функции задается глобальным лексическим окружением, поскольку функция окружена глобальной областью видимости в исходном коде.
Обратите внимание, что когда функция завершается, ее контекст выполнения удаляется из стека, но ее лексическое окружение может быть удалено или не удалено из памяти, в зависимости от того, ссылаются ли на это лексическое окружение другие лексические окружения (в зависимости от того, существуют замыкания или нет). ).
Подробный пример закрытия
Теперь, когда мы понимаем контекст выполнения и лексическое окружение, давайте вернемся к замыканиям.
Пример 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: <globalLexicalEnvironment>
ThisBinding: <Global Object>
}
из-за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,
<anonymous function> : <reference to function>
}
outer: <globalLexicalEnvironment>
}
Эта функция возвращает анонимную функцию и назначает ееcount
Переменная.
count
При выполнении его лексическое окружение будет выглядеть так:
countLexicalEnvironment = {
EnvironmentRecord: {
}
outer: <getCountLexicalEnvironment>
}
count
Когда функция выполняется, движок JavaScript просматривает лексическое окружение этой функции.counter
. Кроме того, поскольку его запись окружения пуста, механизм будет искать внешнее лексическое окружение функции.
Движок находит переменную, выводит ее на консоль и помещаетgetCounter
Переменные в лексическом окружении функции увеличиваются на 1.
Поэтому первый звонокcount
после функцииgetCounter
Лексическое окружение функции выглядит так:
getCounterLexicalEnvironment = {
EnvironmentRecord: {
counter: 1,
<anonymous function> : <reference to function>
}
outer: <globalLexicalEnvironment>
}
при каждом звонкеcount
функции, движок JavaScript создаст новое лексическое окружение для функции, увеличиваяcounter
переменная и обновлениеgetCounter
Лексическое окружение функции отразить изменения.
В заключение
Благодаря приведенному выше объяснению, я полагаю, вы полностью поняли закрытие. Замыкания — это фундаментальная концепция JavaScript, которую должен понимать каждый разработчик JavaScript. Знакомство с этими понятиями поможет вам стать более эффективным и лучшим разработчиком JavaScript.
наконец
Прошлые основные моменты:
- Интервью перед интерфейсом обязательно | Одна статья, чтобы понять область действия и цепочку областей видимости в JavaScript
- Обязательны фронтенд-интервью | Одна статья для понимания контекста выполнения в JavaScript
- IntersectionObserver и отложенная загрузка
- Предварительное изучение принципов браузерного рендеринга
- Блочная модель CSS, макет и содержащие блоки
- Подробная интерпретация приоритета селектора CSS
Подпишитесь на официальный аккаунт, чтобы увидеть больше.