Погружаясь глубже в 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:

  1. Объявление функции:мы определяем функцию outside(x), которая имеет внутреннюю функцию inside(y). Внутренняя функция складывает значения x и y.
  2. Назначение функции. Когда мы это делаем:
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.

Это можно сделать, потому что:

  1. B образует замыкание, включающее A (т. е. B может получить доступ к аргументам и переменным A).
  2. C образует замыкание, включающее B.
  3. Поскольку закрытие 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).

Конец

Довольно короткий для этого блога, потому что я хочу вписаться в «Замыкания» и «Использование объекта аргументов» в следующем посте в блоге. Тогда увидимся!