Замыкания в JavaScript на примерах кода
В Интернете полно отличных объяснений того, “ что такое” замыкание, но лишь немногие глубоко погружаются в “почему”.
Я считаю, что понимание внутренностей в конечном итоге дает разработчикам лучшее понимание своих инструментов, поэтому данная статья будет посвящена гайкам и болтам того, как и почему замыкания работают так, как они работают.
Что такое замыкание?
Замыкание- чрезвычайно мощное свойство JavaScript( и большинства языка программирования). Как определено в MDN:
Замыкания - это функции, которые ссылаются на независимые(свободные) переменные. Другими словами, функция, определенная в замыкании, “запоминает” среду, в которой она была создана
Примечание. Свободные переменные- это переменные, которые не объявляются локально и не передаются в качестве параметра.
Давайте рассмотрим несколько примеров:
Пример 1:
function numberGenerator() {
// Local “free” variable that ends up within the closure
var num = 1;
function checkNumber() {
console.log(num);
}
num++;
return checkNumber;
}
var number = numberGenerator();
number(); // 2
В приведенном выше примере функция numberGenerator создает локальную “свободную” переменную num(число) и checkNumber(функция, выводящую num на консоль).
У функции checkNumber нет собственных локальных переменных, однако у нее есть доступ к переменным внутри внешней функции, numberGenerator, из-за замыкания.
Следовательно, он может использовать переменную num, объявленную в numberGenerator, чтобы успешно вывести ее на консоль даже после возврата numberGenerator.
Пример 2:
В этом примере я покажу, что замыкание содержит любые локальные переменные, которые были объявлены внутри внешней объемлющей функции.
function sayHello() {
var say = function(){ console.log(hello); }
// Local variable that ends up within the closure
var hello = 'Hello, world!';
return say;
}
var sayHelloClosure = sayHello();
sayHelloClosure(); // ‘Hello, world!’
Обратите внимание, как переменная hello определена после анонимной функций, но по-прежнему имеет доступ к переменной hello. Это связано с тем, что переменная hello уже была определена в “области действий” функции во время создания, что делает ее доступной, когда анонимная функция наконец будет выполнена.
(Не волнуйтесь, я объясню, что означает “область охвата” позже в статье. А пока просто действуйте!)
Понимание высокого уровня
Эти примеры иллюстрируют “какие” замыкания на высоком уровне.
Общая тема такова: у нас есть доступ к переменным, определенным в объемлющей функции(функциях), даже после того, как объемлющая функция, которая определяет эти переменные, вернула значение.
Ясно, что в фоновом режиме происходит что-то, что позволяет этим переменным оставаться доступными еще долгое время после того, как объемлющая функция, которая их опередила, возвратилась.
Чтобы понять, как это возможно, нам нужно коснуться нескольких связанных концепций- начать с высоты 3000 футов и медленно спускаться обратно в страну закрытий.
Начнем с всеобъемлющего контекста, в котором запускается функция, известного как “контекст выполнения”.
Контекст выполнения
Контекст выполнения- это абстрактное понятие, используемое спецификацией ECMAScript для отслеживания оценки кода во время выполнения. Это может быть глобальный контекст, в котором ваш код впервые выполняется, или когда поток выполнения входит в тело функции.
В любой момент времени может быть запущен только один контекст выполнения. Вот почему JavaScript является “однопоточным”, то есть одновременно может обрабатываться только одна команда.