Часть 1: Основы
Этот пост был вдохновлен одним из моих любимых пользователей YouTube на JavaScript, Маттиас Петтер Йоханссон, сокращенно MPJ. MPJ ведет канал Fun Fun Function на YouTube (сейчас на перерыве). Он очень опытен и постоянно осваивает новые, иногда трудные территории. Он интересный и умен. Вот что он сказал о закрытии ...
Замыкания - это действительно сложная тема ... и мне потребовалось несколько лет в JavaScript, прежде чем я действительно понял, что происходит. - MPJ (Терапия воздействия закрытия - Изучение закрытий в JavaScript с помощью дружественного живого программирования мобов)
Хорошо, закрытие кажется немного сложным. Как и многие другие аспекты программирования, мы можем понять это интуитивно. Давайте сделаем глубокий вдох ...
… И начнем с самого начала. Согласно Mozilla, закрытие JavaScript определяется как…
Комбинация объединенной функции (en close d) со ссылками на ее окружающее состояние. Другими словами, замыкание дает вам доступ к области внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда создается функция, во время создания функции.
Ух ты. Это определение немедленно начинает проникать в функции внутри функций, от чего у меня кружится голова. Мы вернемся к этому чуть позже.
Сейчас основная идея, которую нужно понять, состоит в том, что функции JavaScript имеют доступ к переменным, которые находятся в области видимости при вызове функции. Например, рассмотрим этот фрагмент:
<script> let alterEgo = "Superman" const sayCatchPhrase = () => { console.log(`This looks like a job for ${ alterEgo }!`) } sayCatchPhrase() </script> console.log ==> This looks like a job for Superman!
Обратите внимание, что функция sayCatchPhrase () вызывается без каких-либо аргументов. Тем не менее, у него есть доступ к глобальной переменной alterEgo, которая определяется вне функции sayCatchPhrase (). Из-за закрытия alterEgo доступно внутри sayCatchPhrase ().
Если бы мы не хотели полагаться на закрытие, нам нужно было бы вместо этого передать alterEgo в качестве аргумента функции, возможно, так:
<script> const sayCatchPhrase = (alterEgo) => { console.log(`This looks like a job for ${ alterEgo }!`) } sayCatchPhrase('Superman') </script> console.log ==> This looks like a job for Superman!
Рассмотрим следующие изменения исходного примера. Сначала мы присваиваем alterEgo значение «Супермен». Затем определяется функция sayCatchPhrase (), затем alterEgo переназначается на Spiderman и, наконец, на sayCatchPhrase () вызывается функция. Кто это работа?
<script> let alterEgo = 'Superman' const sayCatchPhrase = () => { console.log(`This looks like a job for ${ alterEgo }!`) } alterEgo = 'Spiderman' sayCatchPhrase() </script> console.log ==> This looks like a job for Spiderman!
Console.log из sayCatchPhrase () сообщает Spiderman, даже если значение было переназначено после того, как функция была определена. Это связано с тем, что функция sayCatchPhrase () не делает снимок значений в области видимости в момент ее объявления или чего-то подобного. Значение alterEgo внутри функции sayCatchPhrase () будет отражать текущее значение alterEgo при вызове функции. В момент вызова функции sayCatchPhrase (), описанной выше, значением alterEgo является «Spiderman».
Функции внутри функций, о боже!
На первый взгляд может показаться, что замыкания могут стимулировать использование глобальных переменных в JavaScript. Например, программист может подумать о настройке некоторых глобальных переменных в начале сценария. Затем из-за закрытия все функции в скрипте будут иметь доступ к глобальным переменным и могут их изменять. Однако использование глобальных переменных - плохая практика и медленнее. Использование глобальных переменных может быстро привести к ошибкам, поскольку ссылки на переменные сбивают друг друга, а значения переназначаются случайно / неосознанно. Дополнительные аргументы против использования глобальных переменных можно найти в этом сообщении.
Интересно, что замыкания действительно могут помочь предотвратить использование глобальных переменных, потому что функции в JavaScript считаются первоклассными гражданами. Быть первоклассным гражданином означает, что в JavaScript функции могут храниться в переменных, передаваться другим функциям как аргументы и возвращаться из других функций как значения.
Чтобы увидеть, как это связано, мы собираемся вернуть функцию из функции и присвоить ее переменной. Из-за закрытия возвращаемая функция будет иметь доступ к переменным внутри внешней функции.
Возьмем, к примеру, следующий сценарий.
const countdown = () => { let number = 10 return () => { //number is available here because of closure number = number - 1 return number } } //assign the countdown function to a variable, countingDown //we can do this because functions are first class citizens const countingDown = countdown() //invoke countingDown 10 times. number is going down each time. console.log(countingDown()) //=> 10 console.log(countingDown()) //=> 9 console.log(countingDown()) //=> 8 console.log(countingDown()) //=> 7 console.log(countingDown()) //=> 6 console.log(countingDown()) //=> 5 console.log(countingDown()) //=> 4 console.log(countingDown()) //=> 3 console.log(countingDown()) //=> 2 console.log(countingDown()) //=> 1 console.log(countingDown()) //=> 0
Переменная number доступна возвращаемой анонимной функции из функции countdown () из-за закрытия. Однако число недоступно вне функции. Например, если мы попробуем:
console.log('number', number) //=> Uncaught ReferenceError: number is not defined console.log('countdown.number', countdown.number) //=> countdown.number undefined
Числовая переменная фактически является частной функции обратного отсчета, и к ней нельзя получить доступ на протяжении всего скрипта. Используя этот шаблон, мы можем писать функции, которые отслеживают изменение переменных, и можно избежать использования глобальных переменных.
В следующем посте мы углубимся и рассмотрим некоторые распространенные варианты использования закрытий JavaScript.
использованная литература
Забавная функция
Глобальные переменные - это плохо
Замыкания - Часть 5 функционального программирования в JavaScript
Терапия воздействия на закрытие - Изучение закрытий на JavaScript с помощью дружественного программирования в реальном времени < br /> Функции JavaScript - первоклассные граждане