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

Краткий обзор предыдущей функции (из предыдущего сообщения в блоге)
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
const fnInside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give it
console.log(fnInside(5)); // 8
console.log(outside(3)(5)); // 8
Давайте просто разберем эту функцию шаг за шагом. Это прекрасный пример замыкания в JavaScript:
- Объявление функции:мы определяем функцию
outside(x), которая имеет внутреннюю функциюinside(y). Внутренняя функция складывает значенияxиy. - Назначение функции. Когда мы это делаем:
const fnInside = outside(3);
Мы вызываем outside с x = 3 и получаем взамен функцию inside. Самое интересное, что эта функция inside «помнит», что x = 3. В этом суть замыкания. Функция inside имеет доступ к своей собственной области действия, области действия функции outside (где определено x) и глобальной области действия.
3. Вызов внутренней функции. Когда мы делаем:
console.log(fnInside(5));
Мы вызываем функцию inside с y = 5. Он использует значение x, которое он «запоминает» (то есть 3), и добавляет его к 5, в результате чего получается 8. Честно говоря, у меня голова почесалась. Как функция узнает о значении y? Ну, так как на шаге 2 мы определили fnInside как снаружи (3), значение x теперь постоянно 3. Когда вы впоследствии вызываете fnInside(5), вы фактически вызываете функцию inside и устанавливаете y в 5. Ага. Так почему же это работает? Потому что ключевым фактором здесь является тот факт, что return inside; возвращает функцию, а не результат сложения внутри нее.
4. Прямой вложенный вызов. Эта строка:
console.log(outside(3)(5));
Является прямым способом вызова возвращаемой внутренней функции.
Сохранение переменных
Обратите внимание, как сохраняется x при возврате inside. Замыкание должно сохранять аргументы и переменные во всех областях, на которые оно ссылается. Поскольку каждый вызов предоставляет потенциально разные аргументы, для каждого вызова outside создается новое замыкание. Память может быть освобождена только тогда, когда возвращаемый inside больше не доступен.
Многократно вложенные функции
Функции могут быть многократно вложены. Например:
- Функция (
A) содержит функцию (B), которая сама содержит функцию (C). - Обе функции
BиCобразуют замыкания здесь. Таким образом,Bможет получить доступ кA, аCможет получить доступ кB. - Кроме того, поскольку
Cможет получить доступ кB, который может получить доступ кA,Cтакже может получить доступ кA.
Таким образом, замыкания могут содержать несколько областей видимости; они рекурсивно содержат область действия содержащих его функций. Это называется связывание области действия. (Причина, по которой это называется «цепочкой», будет объяснена позже.)
Рассмотрим следующий пример:
function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // Logs 6 (which is 1 + 2 + 3)
В этом примере C обращается к B`s y и A`s x.
Это можно сделать, потому что:
Bобразует замыкание, включающееA(т. е.Bможет получить доступ к аргументам и переменнымA).Cобразует замыкание, включающееB.- Поскольку закрытие
CвключаетB, а закрытиеBвключаетA, то закрытиеCтакже включаетA. Это означает, чтоCможет получить доступ к как аргументам и переменнымB, так иA. Другими словами,Cсвязывает области действияBиAв указанном порядке.
Обратное, однако, неверно. A не может получить доступ к C, потому что A не может получить доступ ни к одному аргументу или переменной B, переменной которой является C. Таким образом, C остается закрытым только для B.
Конфликты имен
Когда два аргумента или переменные в области действия замыкания имеют одно и то же имя, возникает конфликт имен. Более вложенные области имеют приоритет. Таким образом, самая внутренняя область имеет наивысший приоритет, а самая внешняя область — наименьший. Это цепочка областей. Первая цепочка является самой внутренней областью действия, а последняя — самой внешней областью действия. Рассмотрим следующее:
function outside() {
const x = 5;
function inside(x) {
return x * 2;
}
return inside;
}
console.log(outside()(10)); // 20 (instead of 10)
Конфликт имен происходит в операторе return x * 2 между параметром x inside и переменной x outside. Цепочка областей видимости здесь {inside, outside, глобальный объект}. Таким образом, x из inside имеет приоритет над x из outside, и вместо 10 (x из outside) возвращается 20 (x из inside).
Конец
Довольно короткий для этого блога, потому что я хочу вписаться в «Замыкания» и «Использование объекта аргументов» в следующем посте в блоге. Тогда увидимся!