🧾 Замыкание в JavaScript (Часть 1)
JavaScript – язык с сильным функционально-ориентированным уклоном. Он даёт нам много свободы. Функция может быть динамически создана, скопирована в другую переменную или передана как аргумент другой функции и позже вызвана из совершенно другого места.
Мы знаем, что функция может получить доступ к переменным из внешнего окружения, эта возможность используется очень часто. Что произойдет, когда переменные изменятся? Ответим на эти и другие вопросы.
Для начала давайте рассмотрим две ситуации, а затем изучим внутренние механизмы шаг за шагом, чтобы вы смогли ответить на эти и более сложные вопросы в будущем.
1. Функция sayHi использует внешнюю переменную name. Какое значение будет использовать функция при выполнении?
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete";
sayHi(); // что будет показано: "John" или "Pete"?
Такие ситуации распространены и в браузерной и в серверной разработке. Выполнение функции может быть запланировано позже, чем она была создана, например, после какого-нибудь пользовательского действия или сетевого запроса.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
JavaScript – язык с сильным функционально-ориентированным уклоном. Он даёт нам много свободы. Функция может быть динамически создана, скопирована в другую переменную или передана как аргумент другой функции и позже вызвана из совершенно другого места.
Мы знаем, что функция может получить доступ к переменным из внешнего окружения, эта возможность используется очень часто. Что произойдет, когда переменные изменятся? Ответим на эти и другие вопросы.
Для начала давайте рассмотрим две ситуации, а затем изучим внутренние механизмы шаг за шагом, чтобы вы смогли ответить на эти и более сложные вопросы в будущем.
1. Функция sayHi использует внешнюю переменную name. Какое значение будет использовать функция при выполнении?
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete";
sayHi(); // что будет показано: "John" или "Pete"?
Такие ситуации распространены и в браузерной и в серверной разработке. Выполнение функции может быть запланировано позже, чем она была создана, например, после какого-нибудь пользовательского действия или сетевого запроса.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
Замыкание в JavaScript (Часть 2)
Итак, вопрос в том, получит ли она доступ к последним изменениям?
2. Функция makeWorker создаёт другую функцию и возвращает её. Новая функция может быть вызвана откуда-то ещё. Получит ли она доступ к внешним переменным из места своего создания или места выполнения или из обоих?
let name = "Pete";
return function() {
alert(name);
};
}
let name = "John";
// create a function
let work = makeWorker();
// call it
work(); // что будет показано? "Pete" (из места создания) или "John" (из места выполнения)
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
Итак, вопрос в том, получит ли она доступ к последним изменениям?
2. Функция makeWorker создаёт другую функцию и возвращает её. Новая функция может быть вызвана откуда-то ещё. Получит ли она доступ к внешним переменным из места своего создания или места выполнения или из обоих?
function makeWorker() {let name = "Pete";
return function() {
alert(name);
};
}
let name = "John";
// create a function
let work = makeWorker();
// call it
work(); // что будет показано? "Pete" (из места создания) или "John" (из места выполнения)
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
🧾 Замыкание в JavaScript (Часть 3)
Чтобы понять, что происходит, давайте для начала обсудим, что такое «переменная» на самом деле.
В JavaScript у каждой выполняемой функции, блока кода и скрипта есть связанный с ними внутренний (скрытый) объект, называемый лексическим окружением
LexicalEnvironment.
Объект лексического окружения состоит из двух частей:
1. Environment Record – объект, в котором как свойства хранятся все локальные переменные (а также некоторая другая информация, такая как значение this).
2. Ссылка на внешнее лексическое окружение – то есть то, которое соответствует коду снаружи (снаружи от текущих фигурных скобок).
"Переменная" – это просто
свойство специального внутреннего объекта: Environment Record. «Получить или изменить переменную», означает, «получить или изменить свойство этого объекта».
Например, в этом простом коде только одно лексическое окружение: схема.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
Чтобы понять, что происходит, давайте для начала обсудим, что такое «переменная» на самом деле.
В JavaScript у каждой выполняемой функции, блока кода и скрипта есть связанный с ними внутренний (скрытый) объект, называемый лексическим окружением
LexicalEnvironment.
Объект лексического окружения состоит из двух частей:
1. Environment Record – объект, в котором как свойства хранятся все локальные переменные (а также некоторая другая информация, такая как значение this).
2. Ссылка на внешнее лексическое окружение – то есть то, которое соответствует коду снаружи (снаружи от текущих фигурных скобок).
"Переменная" – это просто
свойство специального внутреннего объекта: Environment Record. «Получить или изменить переменную», означает, «получить или изменить свойство этого объекта».
Например, в этом простом коде только одно лексическое окружение: схема.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
🧾 Замыкание в JavaScript (Часть 4)
Это, так называемое, глобальное лексическое окружение, связанное со всем скриптом.
На картинке выше прямоугольник означает Environment Record (хранилище переменных), а стрелка означает ссылку на внешнее окружение. У глобального лексического окружения нет внешнего окружения, так что она указывает на null.
А вот как оно изменяется при объявлении и присваивании переменной: схема.
Прямоугольники с правой стороны демонстрируют, как глобальное лексическое окружение изменяется в процессе выполнения кода:
1. В начале скрипта лексическое окружение пустое.
2. Появляется определение переменной let
3. Переменной phrase присваивается значение.
4. Переменная phrase меняет значение.
Пока что всё выглядит просто, правда?
Итого:
- Переменная – это свойство специального внутреннего объекта, связанного с текущим выполняющимся блоком/функцией/скриптом.
- Работа с переменными – это на самом деле работа со свойствами этого объекта.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
Это, так называемое, глобальное лексическое окружение, связанное со всем скриптом.
На картинке выше прямоугольник означает Environment Record (хранилище переменных), а стрелка означает ссылку на внешнее окружение. У глобального лексического окружения нет внешнего окружения, так что она указывает на null.
А вот как оно изменяется при объявлении и присваивании переменной: схема.
Прямоугольники с правой стороны демонстрируют, как глобальное лексическое окружение изменяется в процессе выполнения кода:
1. В начале скрипта лексическое окружение пустое.
2. Появляется определение переменной let
phrase. У неё нет присвоенного значения, поэтому присваивается undefined.3. Переменной phrase присваивается значение.
4. Переменная phrase меняет значение.
Пока что всё выглядит просто, правда?
Итого:
- Переменная – это свойство специального внутреннего объекта, связанного с текущим выполняющимся блоком/функцией/скриптом.
- Работа с переменными – это на самом деле работа со свойствами этого объекта.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
🧾 Замыкание в JavaScript (Часть 5)
До сих пор мы рассматривали только переменные. Теперь рассмотрим Function Declaration.
В отличие от переменных, объявленных с помощью let, они полностью инициализируются не тогда, когда выполнение доходит до них, а раньше, когда создаётся лексическое окружение.
Для верхнеуровневых функций это означает момент, когда скрипт начинает выполнение.
Вот почему мы можем вызвать функцию, объявленную через Function Declaration, до того, как она определена.
Следующий код демонстрирует, что уже с самого начала в лексическом окружении что-то есть. Там есть say, потому что это Function Declaration. И позже там появится phrase, объявленное через let: схема.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev
До сих пор мы рассматривали только переменные. Теперь рассмотрим Function Declaration.
В отличие от переменных, объявленных с помощью let, они полностью инициализируются не тогда, когда выполнение доходит до них, а раньше, когда создаётся лексическое окружение.
Для верхнеуровневых функций это означает момент, когда скрипт начинает выполнение.
Вот почему мы можем вызвать функцию, объявленную через Function Declaration, до того, как она определена.
Следующий код демонстрирует, что уже с самого начала в лексическом окружении что-то есть. Там есть say, потому что это Function Declaration. И позже там появится phrase, объявленное через let: схема.
📋 #JavaScript #Совет
▬▬▬▬▬▬▬▬
// How To WebDev