В некоторых случаях закрытие трудно предсказать. Собрал некоторые из этих концепций с реализацией кода, чтобы сделать замыкание простым и интересным.
Определение
Функция со ссылкой из внешней среды создает замыкание.
- JS-функции могут получить доступ к своей лексической области (внешней области)
- Функции могут запоминать свою лексическую область видимости независимо от того, где они выполняются.
Пример и настройка
function outer () { var a = 'hello'; function inner() { console.log(a); } return inner; } outer()(); // prints "hello"
Нет необходимости определять переменные над определением функции.
function outer() { function inner() { console.log(a); } var a = 'hello'; // define variable after function definition return inner; } outer()(); // prints 'hello'
Вместо использования var
мы можем использовать let
или const
.
function outer() { function inner() { console.log(a); } const a = 'hello'; return inner; } outer()(); // prints 'hello'
Мы можем передать параметр родительскому методу и создать замыкание.
function outer(b) { function inner() { console.log(b); } return inner; } outer('I am param')(); // prints "I am param"
Закрытие может быть сформировано с помощью нескольких иерархий методов.
function evenOuter() { var val = 'From even outer'; function outer() { function inner() { console.log(val); } return inner; } return outer; } evenOuter()()();
Замыкание взяло переменную области видимости и проигнорировало верхнюю лексическую область видимости,
var val = 'Global scope'; function outer() { function inner() { console.log(val); } var val = 'Outer scope'; return inner; } outer()(); // prints 'Outer scope'
Преимущества закрытия
- Каррирование функций
- Оптимизированная функция высшего порядка
- Запоминание
- Сокрытие и инкапсуляция данных
Каррирование функций
Путем каррирования функций мы можем создать новую функцию для специализированных аргументов и повторно использовать их по мере необходимости.
function multiply(a) { return function(b) { return a * b; } } // Currying the multiply function const double = multiply(2); const triple = multiply(3); console.log(double(5)); // Output: 10 console.log(triple(5)); // Output: 15
Функция высшего порядка
Функция высшего порядка принимает в качестве параметра другую функцию.
function repeat(fn, n) { return function(x) { for (let i = 0; i < n; i++) { fn(x); } } } function logWithPrefix(prefix) { return function(message) { console.log(`${prefix}: ${message}`); } } const repeatLog = repeat(logWithPrefix('INFO'), 3); repeatLog('Hello'); // Output: INFO: Hello (three times)
Запоминание
Аналогично примеру каррирования функций.
Сокрытие данных и инкапсуляция
Рассмотрим следующий код,
let count = 0; function increment() { count += 1 console.log(count); } increment(); count = 2;
Здесь основной поток заключается в том, что мы можем увеличить count
напрямую, не используя метод increment
.
Чтобы всегда использовать increment
для увеличения count
, мы можем использовать замыкание.
function counter() { let count = 0; function increment() { count += 1 console.log(count); } return increment; } const myIncrement = counter(); myIncrement(); // print 1 myIncrement(); // print 2
Интересный факт: каждый раз, когда мы вызываем метод counter
, он всегда создает новую сессию.
function counter() { let count = 0; function increment() { count += 1 console.log(count); } return increment; } const myIncrement1 = counter(); myIncrement1(); // prints 1 myIncrement1(); // prints 2 const myIncrement2 = counter(); myIncrement2(); // prints 1 myIncrement2(); // prints 2
Если нам нужна другая функция, decrement
, то фрагмент кода не масштабируется. В этом случае мы можем использовать Function Constructor
.
function Counter() { let count = 0; this.increment = function() { count += 1 console.log(count); } this.decrement = function () { count -= 1; console.log(count) } } const myCounter = new Counter(); myCounter.increment(); // print 1 myCounter.increment(); // print 2 myCounter.decrement(); // print 1
Недостатки
Если не обращаться должным образом,
- Может вызвать перерасход памяти
- Утечки памяти