Closures

Замыкание — это комбинация функции и лексического окружения, в котором эта функция была определена.

Лексическая область видимости

Рассмотрим следующий пример:

function init() {
    var name = "Mozilla"; // name - локальная переменная, созданная в init
    function displayName() { // displayName() - внутренняя функция, замыкание
        alert (name); // displayName() использует переменную, объявленную в родительской функции    
    }
    displayName();    
}
init();

init()создает локальную переменнуюnameи определяет функциюdisplayName().displayName()— это внутренняя функция, она определена внутри init() и доступна только внутри тела init(). ФункцияdisplayName()не имеет собственных локальных переменных и вместо этого использует переменнуюname, определенную в родительской функции. Однако точно такие же локальные переменные могут создаваться и использоваться внутри displayName().

Выполните этот код и обратите внимание, что команда alert() внутри displayName() благополучно выводит на экран содержимое переменнойname , объявленной в родительской функции. Это пример так называемой лексической области видимости (lexical scoping): в JavaScript область действия переменной определяется по ее расположению в коде (это очевидно лексически), и вложенные функции имеют доступ к переменным, объявленным вовне. Этот механизм и называется Lexical scoping (область действия, ограниченная лексически).

Замыкание

Рассмотрим следующий пример:

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
};

var myFunc = makeFunc();
myFunc();

Если выполнить этот код, то результат будет такой же, как и выполнение init() из предыдущего примера: строка "Mozilla" будет показана в JavaScript alert диалоге. Что отличает этот код и представляет для нас интерес, так это то, что внутренняя функция displayName() была возвращена из внешней до того, как была выполнена.

На первый взгляд кажется не очевидным, что этот код правильный, но он работает. В некоторых языках программирования локальные переменные функции существуют только во время выполнения этой функции. После завершения выполнения makeFunc()можно ожидать, что переменная name больше не будет доступна. Однако, поскольку код продолжает нормально работать, очевидно, что это не так в случае JavaScript.

Причина в том, что функции в JavaScript формируют так называемые замыкания. Замыкание — это комбинация функции и её лексического окружения, в котором эта функция была объявлена. Это окружение состоит из произвольного количества локальных переменных, которые были в области действия функции во время создания замыкания. В рассмотренном примере, myFunc — это ссылка на экземпляр функции displayName , созданной в результате выполнения makeFunc. Экземпляр функции displayName , в свою очередь, сохраняет ссылку на свое лексическое окружение, в котором есть переменная name. По этой причине, когда происходит вызов функции myFunc, переменная name остается доступной для использования, и сохраненный в ней текст "Mozilla" передаётся в alert.

А вот немного более интересный пример — функция makeAdder:

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
};

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

Здесь мы определили функцию makeAdder(x), которая получает единственный аргумент x и возвращает новую функцию. Эта функция получает единственный аргумент y и возвращает сумму x и y.

По существу, makeAdder вместо этого — фабрика функций, она создает функции, которые могут прибавлять определенное значение к своему аргументу. В примере выше мы используем нашу фабричную функцию для создания двух новых функций — одна прибавляет 5 к своему аргументу, вторая прибавляет 10.

add5и add10 — это примеры замыканий. Эти функции делят одно определение тела функции, но при этом они сохраняют различные окружения. В окружении функции add5x — это 5, в то время как в окружении add10x — это 10.

Last updated