Перейти к содержанию

Замыкания

Лучшее, что когда-либо было реализовано в JavaScript - это замыкания. Функция в JavaScript имеет доступ к любой переменной, определенной во внешнем контексте. Это показано на примере ниже:

function outerFunction(arg) {
    var variableInOuterFunction = arg;

    function bar() {
        // Обращение к переменной внешнего контекста
        console.log(variableInOuterFunction);
    }

    // Вызов локальной функции демонстрирует, что arg доступен
    bar();
}

outerFunction('hello closure'); // выводит "hello closure"

Как вы видите, внутренняя функция обращается к переменной (variableInOuterFunction) внешнего контекста. Переменные из внешней функции замкнуты (или связаны) во внутренней функции. Отсюда и термин замыкания. Сама концепция достаточно проста и интуитивна.

А теперь потрясающая часть: Внутренняя функция имеет доступ к переменным внешнего контекста даже после того, как внешняя функция вернула результат выполнения. Это происходит потому что переменные все еще замкнуты во внутренней функции и не зависят от внешней функции. Давайте посмотрим на примере:

function outerFunction(arg) {
    var variableInOuterFunction = arg;
    return function () {
        console.log(variableInOuterFunction);
    };
}

var innerFunction = outerFunction('hello closure!');

// Обратите внимание, что outerFunction
// уже вернула результат выполнения
innerFunction(); // выводит "hello closure"

Почему это круто

Это позволяет легко создавать объекты, например, для паттерна раскрывающегося модуля

function createCounter() {
    let val = 0;
    return {
        increment() {
            val++;
        },
        getVal() {
            return val;
        },
    };
}

let counter = createCounter();
counter.increment();
console.log(counter.getVal()); // 1
counter.increment();
console.log(counter.getVal()); // 2

На высоком уровне это похоже на то, что делает Node.js (Не волнуйтесь, если это не стало понятным прямо сейчас. Понимание придет позже 🌹):

// Псевдо-код для объяснения концепции
server.on(function handler(req, res) {
    loadData(req.id).then(function (data) {
        // `res` замкнут и доступен
        res.send(data);
    });
});