Узнайте о шумихе, стоящей за замыканиями Javascript, и о том, как они могут помочь вам в повседневном программировании.

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

Сборщик мусора

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

Что происходит с innerName после завершения выполнения innerFunction? Это не служит никакой дополнительной цели для нашего кода и, следовательно, является пустой тратой нашего пространства памяти. К счастью, в нашем распоряжении есть ловкий сборщик мусора Javascript (каламбур). Сборщик автоматически очищает пространство памяти, предназначенное для innerName, определяя, что никакой другой код не ссылается на эту конкретную переменную. (Если вы хотите узнать больше о том, как именно работает этот процесс, взгляните на метод 'Mark and Sweep'). По тем же причинам память, выделенная для externalName, будет собрана после удаления externalFunction из вызова. куча.

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

Что такое замыкания?

Давайте взглянем на другую функцию, немного отличающуюся от предыдущей:

На первый взгляд кажется, что здесь происходит что-то странное, вызывается externalFunction и его возвращаемое значение (innerFunction) присваивается functionExpression. Затем externalFunction удаляется из стека вызовов, поэтому интуитивно можно подумать, что externalName исчезнет вместе с ним. Вот тут-то и появляются замыкания — движок Javascript может идентифицировать, что externalName упоминается во внутренней функции, и поэтому сохраняет его в куче памяти, несмотря на то, что externalFunction удаляется из стека вызовов. Концепция замыканий может быть сложной для понимания даже более опытными разработчиками, но не расстраивайтесь, все, что нужно, — это некоторое знакомство с ними.

Как замыкания помогают нам

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

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

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

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

В этом сценарии мы создаем огромный массив только один раз вместо четырех, используя замыкание. Функция findIndex возвращается и назначается getHeavyAction, и нам не нужно снова вызывать HeavyAction2, так как переменная hugeArray хранится в куче памяти.

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