Замыкание в JavaScript — это функциональный объект, который имеет доступ к лексической области видимости другого функционального объекта (его переменным и функциям). Самый простой способ создать замыкание — поместить функцию внутри функции; причина в том, что в JavaScript функция всегда имеет доступ к лексической области видимости содержащей ее функции.

function outerFunction() {
    var outerVar = 'samovar';
    
    function innerFunction() {
        console.log(outerVar);
    }
    
    innerFunction();
}
outerFunction();

OUTPUT
samovar

В приведенном выше примере вызывается outerFunction, которая, в свою очередь, вызывает innerFunction. Обратите внимание, что outerVar доступна для innerFunction, о чем свидетельствует правильное ведение журнала значения outerVar в консоль.

Теперь рассмотрим следующее:

function outerFunction() {
    var outerVar = 'samovar';
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}
var referenceToInnerFunction = outerFunction();
console.log(referenceToInnerFunction());

OUTPUT
samovar

Для referenceToInnerFunction установлено значение outerFunction(), которое просто возвращает ссылку на innerFunction. Когда вызывается referenceToInnerFunction, он возвращает outerVar. Опять же, как и выше, это демонстрирует, что innerFunction имеет доступ к outerVar, переменной outerFunction. Кроме того, интересно отметить, что он сохраняет этот доступ даже после завершения выполнения outerFunction.

Но вот тут все становится по-настоящему интересным. Если бы мы избавились от outerFunction, скажем, установили для него значение null, можно подумать, что referenceToInnerFunction потеряет доступ к значению внешняя переменная. Но это не так.

function outerFunction() {
    var outerVar = 'samovar';
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}
var referenceToInnerFunction = outerFunction();
console.log(referenceToInnerFunction());
outerFunction = null;
console.log(referenceToInnerFunction());

OUTPUT
samovar
samovar

Но как это так? Как referenceToInnerFunction может по-прежнему знать значение outerVar теперь, когда для outerFunction установлено значение null?

Причина, по которой referenceToInnerFunction все еще может получить доступ к значению outerVar, заключается в том, что при первом создании замыкания путем помещения innerFunction внутрь outerFunction, innerFunction добавила ссылку на лексическую область видимости outerFunction (ее переменные и функции) в свою цепочку областей видимости. Это означает, что innerFunction имеет указатель или ссылку на все переменные outerFunction, включая outerVar. Таким образом, даже когда outerFunction завершает выполнение или даже если она удалена или установлена ​​в null, переменные в ее лексической области видимости, такие как outerVar, остаются в памяти из-за ожидающей ссылки на них со стороны innerFunction, которая была возвращена в referenceToInnerFunction. Чтобы действительно освободить outerVar и остальные переменные outerFunction из памяти, вам придется избавиться от этой незавершенной ссылки на них, скажем, установив referenceToInnerFunction также обнулить.

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

function outerFunction() {
    var outerVar = 'samovar';
    
    function innerFunction() {
        console.log(outerVar);
    }
    
    outerVar = 'cheese pancakes!';
    innerFunction();
}
outerFunction();

OUTPUT
cheese pancakes!

Во-вторых, когда замыкание создается, оно сохраняет ссылку на все переменные и функции включающей его функции; он не может выбирать. И тем не менее, замыкания следует использовать экономно или, по крайней мере, осторожно, поскольку они могут интенсивно использовать память; многие переменные могут храниться в памяти еще долго после завершения выполнения содержащей их функции.