Замыкание - это комбинация функции и лексического окружения, в котором эта функция была объявлена ​​¹.

Что там произошло?

Если вы, как и многие другие, думали, что строка 12 вернет 3, эта статья может вам немного помочь. Что происходит, так это то, что каждый раз, когда мы вызываем count(), мы возвращаем новую копию innerCount функции, и каждая копия innerCount полагается на данные, которые находятся вне себя, вне ее собственной области.

Эти данные среды, как вы их называете, имеют решающее значение для выполнения самой функции. Можно сказать, что от него зависит работа. Если при запуске innerCount у нас не будет доступа к counter, тогда вся функция будет нарушена! Но к моменту выполнения innerCount counter больше не доступен для внешнего мира!

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

Если counter был переменной в глобальной области видимости, тогда все счетчики будут увеличивать одну и ту же переменную вместо одного внутри замыкания. Не стесняйтесь опробовать его, извлекая counter до строки 1 выше

Создание фабрики функций как реальный пример

Мы можем воспользоваться поведением закрытия, создав функцию, которая, в зависимости от переданных аргументов, возвращает функцию с другой логикой выполнения. Самый простой пример, который я могу придумать, - это фабрика, которая будет возвращать степень n для заданного числа. Результат будет таким же, как при вызове Math.pow(subject, power)

Этот фрагмент кода создает динамическую функцию, по сути, функцию, алгоритм которой явно не определен в самом коде, а скорее во время выполнения, когда он создается с аргументами, переданными в фабрику. Конечно, в предыдущем примере вы могли подумать, что это бесполезно, и были бы правы, но что вам нужно понять, так это потенциал этой функции.

Теперь мы можем определить функцию, которая дает нам степень двойки, просто вызвав var powerOf2 = powerOfNFactory(2). Внутренний generatedFunction будет закрывать power переменную и будет полностью независим от других вызовов powerOfNFactory. Это означает, что мы можем расширить функциональность и делать такие вещи, как отслеживание количества вызовов нашей динамической функции, добавление мемоизации (кэширование результатов для предотвращения повторное выполнение при вызове с теми же аргументами) и многое другое.

Добавление счетчика

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

Мемоизация

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

Обратите внимание, что мы определяем переменную cache вне определения динамической функции, так же, как мы это делали с counter. Мы будем заполнять этот объект ключами, полученными в качестве параметров при вызове динамической функции, и сохранять значение, чтобы пропустить выполнение в этом упрощенном случае Math.pow(subject, power).

Резюме

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