3 варианта использования замыканий (в JavaScript)

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

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

Что такое закрытие?

Термин «закрытие» относится к функции, которая связана со всем, что было в области видимости, когда она была создана.

Давайте разберемся, почему это не интуитивно понятно.

В приведенном ниже коде все работает именно так, как мы ожидали:

Теперь рассмотрим следующее:

У нас есть две функции: outer и nested. Когда вызывается первая функция, она возвращает вторую. В приведенном выше коде к тому времени, когда мы вызываем nestedFunc (который является ссылкой на функцию nested), функция outer уже вернулась. Мы не ожидаем, что nestedFunc сможет получить доступ к переменной two при ее вызове, поскольку эта переменная не входит в область видимости, когда мы ее вызываем.

И тем не менее, это так. Это закрытие - функция, которая имеет доступ ко всем переменным, которые находились в области видимости, когда она была объявлена.

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

1. Отслеживание состояния / стилей DOM

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

Предположим, у вас есть следующая разметка:

и вы хотели иметь возможность программно изменять стили div, сохраняя при этом его исходные стили на случай, если вы захотите вернуться к ним. Замыкания могут нам помочь:

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

Обратите внимание, что нам нужно назначить window.getComputedStyle новому объекту, иначе каждый раз, когда мы пытаемся получить доступ к свойствам на initialState, значения будут пересчитываться, а исходные стили будут потеряны.

Мы могли бы потреблять это следующим образом:

Мы можем создать столько из них, сколько необходимо для любых элементов DOM, которые мы хотим отслеживать или обновлять.

2. Синглтоны

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

В приведенном ниже примере мы используем шаблон singleton для создания действительно простой службы ведения журнала:

В приведенном выше коде используется IIFE или выражение немедленного вызова функции для возврата объекта с помощью методов info, warning и error. Вот почему нам не нужно было вызывать службу как функцию, то есть LoggingService() - это не функция, а возвращаемое значение функции.

Как мы видим, если мы запустим это, три метода объекта LoggingService имеют доступ к строковым переменным в замыкании. Мы могли бы расширить эту услугу, добавив любые переменные, которые захотим, все они будут доступны для методов журнала.

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

3. Функции высшего порядка

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

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

Вот (несколько) длинная версия:

Мы можем использовать замыкания для создания служебной функции, которая немного упрощает работу с ней:

В этом примере мы используем замыкания, "закрывая" аргумент rounder. После возврата rounder мы не ожидаем, что внутренняя функция будет иметь доступ к параметру places, но эта переменная сохраняется в замыкании.

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

Примечание о частных методах

В JavaScript есть распространенный вариант использования замыканий, которого нет в приведенном выше списке, а именно создание частных переменных и методов. Частный метод - это метод, который доступен только внутри экземпляра класса, поэтому classInstance.privateMethod() выдает ошибку. Эти методы вызываются изнутри другими методами класса, но сами они не доступны напрямую извне.

Другие языки, такие как Java, например, поддерживают частные методы, а также другие модификаторы доступа, которые определяют, как можно получить доступ к определенным методам или переменным. Традиционно в JavaScript не было поддержки частных методов, поэтому для его реализации использовались замыкания.

На момент написания этой статьи есть предложение этапа 3 от TC39 о добавлении поддержки частных методов в JavaScript, поэтому нам больше не нужны закрытие для этого.

Новый синтаксис для объявления частных методов заключается в добавлении # к имени переменной, как показано ниже:

При этом, вот как мы могли бы добиться этого с помощью замыканий:

Замыкания повсюду в JavaScript, а также в других языках (не обязательно функциональных) и являются краеугольным камнем функционального программирования.

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

Я надеюсь, что приведенные выше заметки устранят некоторую путаницу вокруг темы замыканий и вдохновят вас на изучение дополнительных методов функционального программирования!

Ресурсы

Статья MDN о закрытии: https://medium.com/r/?url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FJavaScript%2FClosures

Статья в Википедии о закрытии: https://medium.com/r/?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FClosure_%28computer_programming%29

Предложение полей класса TC39: https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Ftc39%2Fproposal-class-fields

Статья Миско Хевери о синглтонах: https://medium.com/r/?url=http%3A%2F%2Fmisko.hevery.com%2F2008%2F08%2F17%2Fsingletons-are-pathological-liars%2F