Закрыть

Замыкания: Осваиваем замыкания в Javascript. Вы читаете вольный перевод статьи Let’s… | by Sergey Shambir

Содержание

Осваиваем замыкания в Javascript. Вы читаете вольный перевод статьи Let’s… | by Sergey Shambir

Вы читаете вольный перевод статьи Let’s Learn JavaScript Closures

Замыкания — это фундаментальная концепция в Javascript, которую любой опытный разработчик знает и применяет.

В Интернете полно прекрасных объяснений, отвечающих на вопрос “что” делают замыкания, но очень мало объяснений “зачем”.

Я уверен, что понимание внутреннего устройства позволяет разработчику лучше освоить свой инструментарий, и этот пост осветит вопросы “как” и “почему” касательно замыканий.

Искренне надеюсь, что после прочтения статьи вы сможете использовать силу замыканий в своих повседневных задачах. Приступим!

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

Замыкания — это крайне функциональная штука, реализованная в Javascript и в большинстве других языков. Процитируем описание с MDN:

Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция, определённая в замыкании, «запоминает» окружение, в котором она была создана.

Независимые переменные — это все переменные, которые не были переданы как параметры и не были объявлены как локальные. Посмотрим на пример.

Пример 1: numberGenerator

В примере выше функция numberGenerator создаёт локальную переменную num (число), а также локальную функцию checkNumber (функцию, печатающую num в консоль разработчика). Локальная функция checkNumber сама по себе не объявляет локальных переменных, но благодаря механизму замыкания ей доступна переменная из внешнего окружения функции numberGenerator. В результате она может пользоваться переменной num, созданной во время вызова функции numberGenerator, даже после возврата из вызова numberGenerator.

Пример 2: sayHello

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

Заметно, что переменная hello объявлена после анонимной функции, но всё равно доступна для неё. Это происходит из-за ключевого слова var, которое делает переменную доступной сразу во всей области видимости функции сразу после начала вызова (не волнуйтесь, мы расскажем подробно об областях видимости в этом же посте).

Подытожим высокоуровневый смысл замыканий

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

Чтобы понять, как такое возможно, мы начнём восхождение с подножий до вершин, где лежит волшебная долина замыканий. Сперва мы достигнем понимания контекста, в котором происходит вызов функции, известного также как “контекст выполнения” (execution context)

Контекст выполнения

Контекст выполнения — это абстрактная концепция, в рамках которой спецификация языка Javascript, известная как ECMAScript, объясняет модель выполнения кода после запуска. Контекст бывает либо глобальным, с которого начинается исполнения скрипта, либо контекстом выполнения вызова, который начинается с момента входа в тело функции.

Контекст выполнения вызова

В каждый момент времени активен только один контекст выполнения. Именно поэтому Javascript называют “однопоточным”, имея ввиду, что только одна инструкция исполняется в один момент времени. Типичный браузер отслеживает контексты выполнения с помощью стека.

Стек — это структура данных, действующая по принципу “первый добавленный убран последним” (Last In First Out), то есть последний объект, добавленный на стек, окажется на его вершине и вылетит первым при извлечении объекта. Добавлять объекты можно только на вершину стека, и удалять их можно только с вершины.

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

Более того, если в данный момент исполняется код из некоторого контекста выполнения, это ещё не значит, что он должен завершить выполнение до запуска другого потока выполнения. Есть временные интервалы, на которых активный контекст засыпает, и другой контекст выполнения становится активным контекстом. Заснувший контекст выполнения может позднее проснуться на той точке, в которой он ранее заснул. Каждый раз при подобной замене одного контекста выполнения другим будет создаваться новый контекст выполнения, который попадёт на вершину стека, и станет активным контекстом.

Посмотрите на практический пример, показывающий работу этой концепции в браузере:

В момент выполнения функции boop вы можете увидеть активный контекст выполнения в отладчике браузера:

Когда вызов функции boop возвращает управление, его контекст выполнения выталкивается из стека, и выполнение продолжается в контексте выполнения вызова bar:

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

  • Состояние вычисления (code evaluation state): всё состояние контекста, необходимое для исполнения, засыпания и продолжения вычисления кода, связанного с одним контекстом выполнения
  • Функция (function): объект функции, с вызовом которой связан контекст выполнения, либо null, если контекст выполнения связан со скриптом или модулем
  • Область (realm): набор внутренних объектов, глобальное окружение ECMAScript и связанные с ним ресурсы, а также весь код на ECMAScript, который загружается в области видимости глобального окружения
  • Лексическое окружение: используется для сопоставления “идентификаторов-ссылок”, используемых в коде внутри контекста выполнения
  • Таблица переменных (variable environment) — таблица, связанная с лексическим окружением, в которой в качестве ключей занасены все имена переменных, используемые в инструкциях объявления переменных

Если всё это выглядит черезчур сложным, не беспокойтесь: среди всех этих сущностей интереснее всего для нас лексическое окружение, потому что именно оно участвует в процессе сопоставления “идентификаторов-ссылок”, созданных в коде внутри контекста выполнения. Идентификаторы — это имена сущностей, таких как переменные и функции. Напомним, что наша первоначальная цель — понять, почему мы можем обращаться к созданным переменным даже после возврата из функции (т.е. возврата из контекста выполнения). Для понимания нам пригодится лексическое окружение!

Примечание: технически, и таблица переменных, и лексическое окружение нужны для реализации замыкания. Но мы можем упростить модель, объединив обе сущности под одним термином “Окружение” (Environment). А если вы желаете подробно изучить разницу между лексическим окружением и таблицей переменных, есть отличная статья от Alex Rauschmayer.

Лексическое окружение (Lexical Environment)

Из определения:

Лексическое окружение — это термин, описывающий связывание идентификаторов с переменными и функциями, основанное на лексической вложенности кода, написанного на ECMAScript (Javascript). Лексическое окружение состоит из таблицы символов и ссылки на внешнее лексическое окружение (нулевой для глобального окружения). Обычно лексическое окружение связано с синтаксической конструкцией в коде на ECMAScript, такой как объявление функции, блок кода или блок catch в try-инструкции, и новое лексическое окружение создаётся каждый раз при вычислении такого кода — ECMAScript-262/6.0.

Давайте разберём его по пунктам:

  • описывающий связывание идентификаторов с переменными и функциями”: лексическое окружение отвечает за управление данными в виде переменных и функций (или, если кратко, “символов”) в коде. Другими словами, оно задаёт смысл доступных идентификаторов. Например, если мы имеем строку кода console.log(x / 10), то смысл использования идентификатора x появляется, если кто-то задаёт связь между этим идентификатором и символом. Лексическое окружение задаёт связь идентификаторов с переменными и функциями через таблицу символов.
  • Лексическое окружение состоит из таблицы символов”: таблица символов в виде ассоциативного массива (хеш-массива или бинарного дерева) даёт простой способ хранения записей обо всех идентификаторах и их привязки к символам внутри лексического окружения. Каждое лексическое окружение имеет таблицу символов.
  • основанное на лексической вложенности кода”: это интересный нюанс, означающий, что вложенное окружение ссылается на внешнее окружение, а это внешнее окружение, в свою очередь, может иметь ссылку на окружающее его окружение. И только глобальное окружение не имеет внешнего окружения. Это можно сравнить с луковицей: есть внешний слой, и все остальные слои вложены друг в друга

В псевдокоде можно описать окружение следующим образом:

  • новое лексическое окружение создаётся каждый раз при вычислении такого кода”: каждый раз при вызове функции создаётся новое лексическое окружение. Это важно — и мы вернёмся к данной оговорке ближе к концу. Примечание: функция — не единственный способ создать лексическое окружение. Оно также создаётся в блоке кода, окружённом фигурными скобками, и в теле ветки “catch” инструкции “try”. В данном посте мы сосредоточимся на окружениях, создаваемых при вызове функции.

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

Цепочка областей видимости (Scope Chain)

На основе определения выше легко понять, что окружение получает доступ к символам родительского окружения, а родительское окружение имеет доступ к своему родителю, и так далее. Объединённое множество идентификаторов, доступ к которым имеет текущее окружение, носит имя “область видимости” (scope). В процессе движения от родительского окружения к дочернему число доступных идентификаторов возрастает, и сами области видимости складываются в “цепочку областей видимости”.

Рассмотрим пример такой вложенности:

Легко заметить, что функция bar вложена внутрь функции foo. На схеме показана визуализация этой иерархии:

Иерархия цепочки областей видимости (т. е. цепочки окружений, связанных с функциями) сохраняется в момент создания функции перед началом интерпретации скрипта. То есть иерархия определена статически расположением в исходном коде (это также называется “лексическим связыванием”, англ. lexical scoping).

Давайте кратко пройдёмся по различиям между “динамической областью видимости” и “статической областью видимости”, что поможет понять, как статические области видимости (или лексические области видимости) позволяют реализовать замыкание.

Обзор: динамические и статические области видимости

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

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

Два примера ниже иллюстрируют разницу между динамическими и статическими областями видимости.

Пример 1:

Со статическими областями видимости возвращаемое bar() значение зависит от переменной x, доступной в момент создания foo(). Из-за лексической структуры кода идентификатор x в функции foo() ссылается на переменную x, инициализированную числом 10.

С динамическим областями видимости всё зависит от стека объявлений переменных, формируемого во время выполнения, поэтому значение x зависит от состояния программы перед вызовом foo(). При вызове bar() на стек объявлений переменных добавляется новая запись о переменной x, инициализированной числом 2, и в результате foo() вернёт 7.

Пример 2:

Принцип тот же — если области видимости динамические, то значение и местоположение переменной myVar определяется в момент вызова функции. Если же области видимости статические, то связывание переменных с идентификаторами в коде происходит в момент создания объекта функции, то есть при выполнении кода с объявлением функции. Объявление функций в старом стиле начинается со слова function, а в новой версии языка появились arrow-функции.

Вы наверняка заметили, что динамические области видимости вызывают некоторую неоднозначность: при чтении кода не очевидно, в какую переменную обратится идентификатор в коде.

Замыкания (Closures)

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

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

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

Вернёмся снова к примеру с вложенными функциями:

Теперь мы понимаем, как работают области видимости, и на основе этих данных можем описать в псевдокоде окружения в этом примере:

После вызова функции test мы получим 45, что является значением, возвращённым функцией bar, поскольку foo вернула объект функции bar. Функция bar имеет доступ к локальной переменной y из функции foo, а также имеет доступ к переменной x, поскольку функция foo имеет доступ к глобальной области видимости. По-английски это называется “scope-chain lookup”, т.е. поиск по цепочке областей видимости.

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

На деле в Javascript происходит иначе: при замыкании данные родительского контекста удерживаются в памяти в области, называемой “кучей” (она называется так из-за структуры данных, которую операционная система использует для реализации кучи). Это позволяет сохранять контекст выполнения даже после того, как его выкинули со стека.

Понравилось? Отлично! Мы достаточно освоили абстрактные механизмы, и можем взглянуть ещё на несколько деревьев.

Пример 1:

Это одна из классических ошибок — у нас есть цикл for и мы пытаемся воспользоваться счётчиком цикла в анонимной функции внутри цикла:

Используя только что усвоенный материал, мы легко обнаружим ошибку: давайте визуализируем окружение после выполнения цикла for в этом примере:

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

Мы исправили проблему! Но есть и более правильный путь: использовать ключевое слово let, которое иначе объявляет переменную — запись о переменной создаётся на каждой итерации цикла.

Example 2:

В этом примере мы покажем как каждый вызов функции создаёт новое замыкание:

В этом примере мы видим, что каждый вызов функции iCantThinkOfAName создаёт новое замыкание, достаточно взглянуть на foo или bar. Последующие любого из замыканий обновляют переменные внутри замыкания, что демонстрирует возможность использования переменных из любого замыкания в функции doSomething уже после того, как функция iCantThinkOfAName возвращает управление.

Example 3:

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

Благодаря тому что функции add и subtract хранят ссылку на окружение функции mysteriousCalculator, они могут использовать переменные из этого окружения для вычисления результата.

Example 4:

В последнем примере мы покажем важное применение замыканий: хранение приватной ссылки на переменную из внешней области видимости:

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

Подытожим

  • Контекст выполнения (execution context) — это абстракция, используемая спецификацией язык ECMAScript для сопровождения выполнения кода; в каждый момент времени только один контекст выполнения выполняет код
  • Каждый контекст выполнения имеет лексическое окружение (lexical environment), которое хранит таблицу привязки имён переменных к их значениям, а также удерживает ссылку на родительское окружение.
  • Набор идентификаторов, доступных из окружения, называется областью видимости. Мы можем вкладывать области видимости друг в друга, создавая цепочку областей видимости
  • Каждая вызванная функция имеет контекст выполнения, который включает в себя ссылку на лексическое окружение, определяющее смысл переменных в функции и хранящее ссылку на родительское окружение. Фактически, функция “запоминает” данное окружение благодаря сохранению ссылки на родительское окружение. Это и называется замыканием.
  • Замыкание для вложенной функции создаётся каждый раз, когда вызывается внешняя функция. Другими словами, замыкание создаётся ещё до выполнения вложенной функции.
  • Область видимости для замыкания является лексической, то есть определяется статически положением функции в исходном коде
  • Замыкания имеют огромное практическое значение. Один из классических приёмов — хранить ссылку на приватную переменную, недоступную для изменения из внешнего контекста.

Заключительные заметки

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

Дальнейшее чтение

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

Замыкания — JavaScript | MDN

Замыкание — это комбинация функции и лексического окружения, в котором эта функция была определена. Другими словами, замыкание даёт вам доступ к Scope (en-US) внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз при создании функции, во время её создания.

Рассмотрим следующий пример:

function init() {
    var name = "Mozilla"; 
    function displayName() { 
        alert (name); 
    }
    displayName();
}
init();

init() создаёт локальную переменную name и определяет функцию displayName(). displayName() — это внутренняя функция — она определена внутри init() и доступна только внутри тела функции init(). Обратите внимание, что функция displayName() не имеет никаких собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций, 

displayName() может иметь доступ к переменной name, объявленной в родительской функции init().

Выполните этот код и обратите внимание, что команда alert()  внутри displayName() благополучно выводит на экран содержимое переменной name объявленной в родительской функции. Это пример так называемой лексической области видимости (lexical scoping): в JavaScript область действия переменной определяется по её расположению в коде (это очевидно лексически), и вложенные функции имеют доступ к переменным, объявленным вовне. Этот механизм и называется Lexical scoping (область действия, ограниченная лексически).

Рассмотрим следующий пример:

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
};

var myFunc = makeFunc();
myFunc();

Если выполнить этот код, то результат будет такой же, как и выполнение init() из предыдущего примера: строка «Mozilla» будет показана в JavaScript alert диалоге. Что отличает этот код и представляет для нас интерес, так это то, что внутренняя функция displayName() была возвращена из внешней до того, как была выполнена.

На первый взгляд, кажется неочевидным, что этот код правильный, но он работает. В некоторых языках программирования локальные переменные-функции существуют только во время выполнения этой функции. После завершения выполнения makeFunc() можно ожидать, что переменная name больше не будет доступна. Однако, поскольку код продолжает нормально работать, очевидно, что это не так в случае JavaScript.

Причина в том, что функции в JavaScript формируют так называемые замыкания.

Замыкание — это комбинация функции и лексического окружения, в котором эта функция была объявлена. Это окружение состоит из произвольного количества локальных переменных, которые были в области действия функции во время создания замыкания. В рассмотренном примере myFunc — это ссылка на экземпляр функции displayName, созданной в результате выполнения makeFunc. Экземпляр функции displayName в свою очередь сохраняет ссылку на своё лексическое окружение, в котором есть переменная name.  По этой причине, когда происходит вызов функции myFunc, переменная name остаётся доступной для использования и сохранённый в ней текст «Mozilla» передаётся в alert.

А вот немного более интересный пример — функция makeAdder:

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
};

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  
console.
log(add10(2));

Здесь мы определили функцию makeAdder(x), которая получает единственный аргумент x и возвращает новую функцию. Эта функция получает единственный аргумент y и возвращает сумму x и y.

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

add5 и add10 — это примеры замыканий. Эти функции делят одно определение тела функции, но при этом они сохраняют различные окружения. В окружении функции add5

x — это 5, в то время как в окружении add10 x — это 10.

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

Следовательно, замыкания можно использовать везде, где вы обычно использовали объект с одним единственным методом.

Такие ситуации повсеместно встречаются в web-разработке. Большое количество front-end кода, который мы пишем на JavaScript, основано на обработке событий. Мы описываем какое-то поведение, а потом связываем его с событием, которое создаётся пользователем (например, клик мышкой или нажатие клавиши). При этом наш код обычно привязывается к событию в виде обратного/ответного вызова (callback): callback функция — функция выполняемая в ответ на возникновение события

.

Давайте рассмотрим практический пример: допустим, мы хотим добавить на страницу несколько кнопок, которые будут менять размер текста. Как вариант, мы можем указать свойство font-size на элементе body в пикселах, а затем устанавливать размер прочих элементов страницы (таких, как заголовки) с использованием относительных единиц em:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h2 {
  font-size: 1. 5em;
}

h3 {
  font-size: 1.2em;
}

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

Используем следующий JavaScript:

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
};

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

Теперь size12, size14, и size16 — это функции, которые меняют размер текста в элементе body на значения 12, 14, и 16 пикселов, соответственно. После чего мы цепляем эти функции на кнопки примерно так:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
<a href="#">12</a>
<a href="#">14</a>
<a href="#">16</a>

Языки вроде Java позволяют нам объявлять частные (private) методы . Это значит, что они могут быть вызваны только методами того же класса, в котором объявлены.

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

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

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

alert(Counter. value()); 
Counter.increment();
Counter.increment();
alert(Counter.value()); 
Counter.decrement();
alert(Counter.value()); 

Тут много чего поменялось. В предыдущем примере каждое замыкание имело свой собственный контекст исполнения (окружение). Здесь мы создаём единое окружение для трёх функций: Counter.increment, Counter.decrement, и Counter.value.

Единое окружение создаётся в теле анонимной функции, которая исполняется в момент описания. Это окружение содержит два приватных элемента: переменную privateCounter и функцию changeBy(val). Ни один из этих элементов не доступен напрямую, за пределами этой самой анонимной функции. Вместо этого они могут и должны использоваться тремя публичными функциями, которые возвращаются анонимным блоком кода (anonymous wrapper), выполняемым в той же анонимной функции.

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

Благодаря механизму lexical scoping в Javascript, все они имеют доступ к переменной privateCounter и функции changeBy.

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

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
alert(Counter1.value()); 
Counter1.increment();
Counter1.increment();
alert(Counter1.value()); 
Counter1.decrement();
alert(Counter1.value()); 
alert(Counter2.
value());

Заметьте, что счётчики работают независимо друг от друга. Это происходит потому, что у каждого из них в момент создания функцией makeCounter() также создавался свой отдельный контекст исполнения (окружение). То есть приватная переменная privateCounter в каждом из счётчиков это действительно отдельная, самостоятельная переменная.

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

До того, как в версии ECMAScript 6 ввели ключевое слово let, постоянно возникала следующая проблема при создании замыканий внутри цикла. Рассмотрим пример:

<p>Helpful notes will appear here</p>
<p>E-mail: <input type="text" name="email"></p>
<p>Name: <input type="text" name="name"></p>
<p>Age: <input type="text" name="age"></p>
function showHelp(help) {
  document. getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес e-mail'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (Вам должно быть больше 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

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

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

Проблема в том, что функции, присвоенные как обработчики события onfocus, являются замыканиями. Они состоят из описания функции и контекста исполнения (окружения), унаследованного от  функции setupHelp. Было создано три замыкания, но все они были созданы с одним и тем же контекстом исполнения. К моменту возникновения события onfocus цикл уже давно отработал, а значит, переменная item (одна и та же для всех трёх замыканий) указывает на последний элемент массива, который как раз в поле возраста.

В качестве решения в этом случае можно предложить использование функции, фабричной функции (function factory), как уже было описано выше в примерах:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес e-mail'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (Вам должно быть больше 16)'}
    ];

  for (var i = 0; i < helpText. length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

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

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

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

Давайте рассмотрим не очень практичный, но показательный пример:

function MyObject(name, message) {
  this. name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

Поскольку вышеприведённый код никак не использует преимущества замыканий, его можно переписать следующим образом:

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

Методы вынесены в прототип. Тем не менее, переопределять прототип — само по себе является плохой привычкой, поэтому давайте перепишем всё так, чтобы новые методы просто добавились к уже существующему прототипу.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype. getMessage = function() {
  return this.message;
};

Код выше можно сделать аккуратнее:

function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}
(function() {
    this.getName = function() {
        return this.name;
    };
    this.getMessage = function() {
        return this.message;
    };
}).call(MyObject.prototype);

В обоих примерах выше методы определяются один раз — в прототипе. И все объекты, использующие данный прототип, будут использовать это определение без дополнительного расхода вычислительных ресурсов. Смотрите подробное описание в статье Подробнее об объектной модели.

Замыкания в JavaScript — Блог HTML Academy

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

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

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

Надеюсь, после этой статьи вы будете лучше подготовлены к использованию замыканий в своей повседневной разработке. Давайте начнём.

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

Замыкания являются мощным инструментом в JavaScript и других языках программирования. Вот определение с MDN:

Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция, определённая в замыкании, «запоминает» окружение, в котором она была создана.

Заметка: cвободные переменные — это переменные, которые не объявлены локально и не передаются в качестве параметра.

Давайте посмотрим на несколько примеров:

Пример 1

В примере функция numberGenerator создаёт локальную «свободную» переменную num (число) и checkNumber (функция, которая выводит число в консоль). Функция checkNumber не содержит собственной локальной переменной, но благодаря замыканию она имеет доступ к переменным внутри внешней функции, numberGenerator. Поэтому объявленная в numberGenerator переменная num будет успешно выведена в консоль, даже после того, как numberGenerator вернёт результат выполнения.

Пример 2

В этом примере видно, что замыкания содержат в себе все локальные переменные, которые были объявлены внутри внешней замкнутой функции — enclosing function.

Обратите внимание, как переменная hello определяется после анонимной функции, но эта функция всё равно может получить доступ к этой переменной hello. Это происходит из-за того, что переменная hello во время создания уже была определена в области видимости (scope), тем самым сделав её доступной на тот момент, когда анонимная функция будет выполнена. Не беспокойтесь, позже я объясню, что такое «область видимости». А пока просто смиритесь с этим.

Понимаем высокий уровень

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

Чтобы понять, как это работает, давайте рассмотрим несколько связанных между собой идей. Мы зайдём издалека и постепенно вернёмся к замыканиям. Начнём наш путь с общего контекста, в котором выполняется функция, и известного как контекст выполнения — execution context.

Замыкания — только часть продвинутого JavaScript

Пройдите курс «JavaScript. Архитектура клиентских приложений» и прокачайте свои навыки. Скидка —1000 ₽ по промокоду SKUCHNO.

Класс, расскажите

Нажатие на кнопку — согласие на обработку персональных данных

Контекст выполнения

Контекст выполнения — это абстрактное понятие, которое используется в спецификации ECMAScript для оценки времени выполнения кода. Это может быть глобальный контекст — global context, в котором ваш код выполнится первым, или когда поток выполнения переходит в тело функции.

В любой момент времени выполняется только один контекст функции (тело функции). Вот почему JavaScript является однопотоковым, так как единовременно может выполняться только одна команда. Обычно браузеры поддерживают этот контекст с помощью стека — stack. Стек — структура данных, выполняемая в обратном порядке: LIFO — «последним пришёл — первым вышел». Последнее, что вы добавили в стек, будет удалено первым из него. Это происходит из-за того, что мы можем только добавить или удалить элементы из верхушки стека. Текущий или «выполняющийся» контекст исполнения — всегда верхний элемент стека. Он выскакивает из стека, когда код в текущем контексте полностью разобран, позволяя следующему верхнему элементу стека взять на себя контекст выполнения.

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

Для наглядности запустите в консоли код, который вы видите ниже:

Затем, когда boop возвратится, он удалится из стека, и bar продолжит работу:

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

  • Оценка состояния кода — любое состояние необходимо выполнить, приостановить и возобновить определение кода, связанного с этим контекстом выполнения.
  • Функция — объект функции, который оценивает контекст выполнения или null, если контекст был определён как script или модуль.
  • Область — набор внутренних объектов, глобальное окружение ECMAScript, весь код ECMAScript, который находится в пределах этого глобального окружения и другие связанные с ним состояния и ресурсы.
  • Лексическое окружение — используется для разрешения ссылок идентификатора кода в этом контексте исполнения.
  • Переменное окружение — лексическое окружение, чья запись окружения — EnvironmentRecord имеет связи, созданные заявленными переменными — VariableStatements в этом контексте выполнения.

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

Заметка: c технической точки зрения, окружение переменных и лексическая область видимости используются для реализации замыканий. Но для простоты мы заменим его на «окружение». Для детального объяснения разницы между лексическим и переменным окружением читайте статью Акселя Раушмайера.

Лексическая область видимости

Дадим определение: лексическое окружение — специфичный тип, используемый для связи идентификаторов с определёнными переменными и функциями на основе лексической структуры вложенности кода ECMAScript. Лексическое окружение состоит из записи окружения и, возможно, нулевой ссылки на внешнее лексическое окружение. Обычно лексическое окружение связано с определённой синтаксической структурой, например: FunctionDeclaration — объявление функции, BlockStatement — оператор блока, Catch clause — условный оператор, TryStatement — перехват ошибок и новым лексическим окружением, которое создавалось каждый раз при разборе кода. — ECMAScript-262/6.0

Давайте разберём это.

  • «используемый для связи идентификаторов»: целью лексического окружения является управление данными, то есть идентификаторами в коде. Говоря иначе, это придаёт им смысл. Например, у нас есть такая строка в консоли: console.log(x / 10), x здесь бессмысленная переменная или идентификатор без чего-либо, что придавало бы ей смысл. Лексическое окружение обеспечивает смысл или «ассоциацию» через запись окружения. Смотрите ниже.
  • Лексическое окружение состоит из записи окружения: запись окружения — причудливый способ сказать, что она хранит записи всех идентификаторов и связей, которые существуют в лексической области видимости. Каждая лексическая область видимости имеет собственную запись окружения.
  • Лексическая структура вложенности: самый интересный момент, который говорит, что внутреннее окружение ссылается на внешнее окружение, и это внешнее окружение может иметь собственное внешнее окружение. В результате окружение может быть внешним окружением для более чем одного внутреннего окружения. Глобальное окружение является единственным лексическим окружением, которое не имеет внешнего окружения. Это сложно описать словами, поэтому давайте использовать метафоры и представим лексическое окружение как слои лука: глобальная среда — внешний слой луковицы, где каждый последующий слой находится ниже.

Так выглядит окружение в псевдокоде:

  • Новое лексическое окружение, которое создавалось каждый раз при разборе кода — каждый раз, когда вызываются внешние вложенные функции, создаётся новое лексическое окружение. Это важно, и мы вернёмся к этому моменту в конце. Примечание: функции — не единственный способ создать лексическое окружение. Другие типы содержат в себе оператор блока — block statement или условный оператор — catch clause. Для простоты, я сосредоточусь на окружении созданной нами функции на протяжении всего поста.

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

Цепочки областей видимости

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

Давайте рассмотрим эту структуру вложенности:

Как вы можете видеть, bar вложен в foo. Чтобы всё это представить посмотрите на диаграмму ниже:

Мы вернёмся позже к этому примеру.

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

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

Идём в обход: динамическая область видимости против статической области видимости

У динамических языков программирования существует стековая архитектура — stack-based implementations, локальные переменные и функции хранятся в стеке. Поэтому, во время выполнения стека, программа определяет какую переменную вы имеете в виду. С другой стороны, статическая область видимости — это когда переменные ссылаются на контекст и фиксируются на момент создания. Другими словами, структура исходного кода программы определяет к каким переменным вы обращаетесь.

Вы могли задаваться вопросом, как различаются динамическая и статическая области видимости. Вот два примера, которые помогут это продемонстрировать:

Пример 1

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

В статической области видимости возврат значения bar зависит от значения x. Это происходит из-за того, что статическая и лексическая структура исходного кода приводит x и к 10, и к 15.

Динамическая область видимости даёт нам стек определённых переменных, которые отслеживаются во время выполнения. Поэтому x, которую мы используем, зависит от того, что находится в её области видимости и как она была динамично определена во время выполнения. Выполнение функции bar выталкивает x = 2 на верхушку стека, заставляя foo вернуть 7.

Пример 2

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

Как вы можете заметить, динамическая область видимости часто создаёт некоторую двусмысленность. Она не даёт точно понять, какая свободная переменная будет передана.

Замыкания

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

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

Кажется, как будто функция «запоминает» это окружение, поскольку функция буквально имеет ссылку к области видимости и переменным, определённым в этой среде.

Возвратимся к примеру вложенной структуры:

Основываясь на нашем понимании того, как работает окружение, мы можем сказать, что определение окружения для вышеупомянутого примера выглядит примерно так в псевдокоде:

Когда мы вызываем функцию test, мы получаем 45, и она возвращает значение из вызова функции bar (потому что foo возвращает bar). bar имеет доступ к свободной переменной y даже после того, как функция foo вернётся, так как bar имеет ссылку на y через его внешнее окружение, которое является окружением foo! bar  так же имеет доступ к глобальной переменной x потому, что у окружения foo есть доступ к глобальному окружению. Это называют «поиск цепочки области видимости».

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

Теперь, когда мы понимаем внутренности на абстрактном уровне, давайте рассмотрим ещё пару примеров:

Пример 1

Вот типичное заблуждение: в цикле for мы пробуем связать переменную счётчика с какой-либо функцией.

На основе всего что было ранее, мы можем с лёгкостью найти ошибку здесь. Абстрактно говоря, вот так выглядит окружение, во время выхода из цикла for:

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

У нас есть только один способ исправить это — создать дополнительный вызываемый контекст для каждой функции, чтобы у них появился собственный контекст/область видимости.

Ура, мы исправили это.

Есть ещё одно решение, в котором мы используем let вместо var, let находится в операторе блока и поэтому новая привязка идентификатора замыкания создаётся для каждой итерации в цикле for.

Та-дам!

Пример 2

В этом примере мы покажем как каждый вызов в функции создаёт новое отдельное замыкание:

Мы видим что каждый вызов в функции iCantThinkOfAName создаёт новое замыкание, а именно foo и bar. Последующие вызовы каждой замкнутой функции обновляют замкнутые переменные в пределах самого замыкания, демонстрируя, что переменные в каждом замыкании используются функции iCantThinkOfAName’s doSomething после того, как вернулась iCantThinkOfAName.

Пример 3

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

Это происходит из-за того, что наши функции add и substract ссылаются на среду mysteriousCalculator, и они в состоянии использовать переменные этой среды для расчёта результата.

Пример 4

Последний пример продемонстрирует важность использования замыканий: для поддержания собственной ссылки на переменную во внешней области видимости.

Это очень мощная техника — она даёт замыкающей функции guessPassword исключительный доступ к переменной password, делая невозможным доступ к password снаружи.

Tl;dr

  • Контекст выполнения — это абстрактный контекст, использовавшийся в спецификации ECMAScript для отслеживания времени выполнения кода. В любое время может быть только один контекст выполнения, который выполняет код.
  • Каждый контекст исполнения имеет лексическое окружение. Оно содержит связи идентификаторов, то есть переменные и их значения и имеет ссылку на него во внешнем окружении.
  • Набор идентификаторов, к которым у каждого окружения есть доступ, называют «область видимости». Мы можем вложить эти области в иерархическую цепь окружения, известной как «цепочки области видимости».
  • Каждая функция имеет контекст выполнения, который включает в себя лексическое окружение. Это придаёт смысл переменным в пределах этой функции и ссылку на родительское окружение. И это означает, что функции «запоминают» окружение или область видимости, так как они буквально ссылаются на это окружение. Это и есть замыкание.
  • Замыкания создаются каждый раз при вызове внешней функции. Другими словами, внутренняя функция не будет возвращена для замыкания, в котором была создана.
  • Область видимости замыканий в JavaScript лексическая, её смысл определяется статично в зависимости от нахождения в исходном коде.
  • Есть множество практических случаев использования замыканий. Один из важных случаев использования — это сохранение приватных ссылок к переменным во внешней среде.

Замыкающая ремарка

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

Дополнительная литература

Для краткости я опустила несколько тем, которые могут быть интересны для некоторых читателей. Вот несколько ссылок, которыми я хотела бы поделиться:

Есть что-то ещё? Предлагайте.

Замыкания — Python: Функции

Python: Функции

Замыкания

Помните функцию inner, которую мы создавали в первом уроке по ФВП. Так вот, эта функция была замыканием! Замыкание (closure), это такая функция, которая ссылается на локальные переменные (использует их в своём теле) в области видимости, в которой она была создана. Этим замыкание отличается от обычной функции, которая может использовать только свои аргументы и глобальные переменные. Рассмотрим пример, демонстрирующий замыкание, и уже на нём разберём что и чем является:

G = 10

def make_closure():
    a = 1
    b = 2
    def inner(x):
        return x + G + a
    return inner

В этом примере inner — замыкание. Переменная b не используется в теле inner и замыканием запомнена не будет. А вот переменная a, напротив, участвует в формировании результата вызова inner и поэтому её значение будет запомнено. А вот G, это глобальная переменная (если принять факт, что указанный код находится на верхнем уровне модуля, т.е. не вложен ни в какие другие блоки).

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

Момент запоминания значений переменных

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

>>> def make_closure():
...     y = 1
...     def inner(x):

Замыкания в Javascript

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

Давайте начнем.

Итак представьте, что у нас есть функция, которая добавляет 2 к числу, которое мы указали в функции.

var addTo = function (passed) {
  var inner = 2
  return passed + inner
}

console.log(addTo(3))

То есть при вызове функции с аргументом 3 мы добавляем 3 + 2 и получаем 5.

Если мы посмотрим в браузер, то оно так и работает.

Теперь изменим код. Давайте уберем аргумент passed. Теперь вопрос: «Откуда мы можем его получить, чтобы он появился внутри функции addTo»?

Мы можем написать passed, как переменную вверху нашей функции и тогда она будет доступна внутри нашей функции.

var passed = 3

var addTo = function () {
  var inner = 2
  return passed + inner
}

console.log(addTo())

Если мы посмотрим в браузер, то мы получаем такой же результат, как до этого.

В javascript все переменные, который описаны снаружи функций доступны внутри этих функций. Именно поэтому мы можем брать переменную passed снаружи и использовать ее внутри нашей функции.

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

var addTo = function (passed) {
  var add = function () {

  }
}

И вернем созданную функцию не вызывая ее.

var addTo = function (passed) {
  var add = function () {

  }

  return add
}

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

var addTo = function (passed) {
  var add = function (inner) {
    return passed + inner
  }

  return add
}

Итак еще раз. Мы описали функцию внутри функции, которая принимает аргумент inner и возвращает passed + inner. passed мы берем извне.

Теперь давайте напишем

var addThree = addTo(3)
console.log(addThree)

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

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

var addThree = addTo(3)
console.dir(addThree)

Теперь в консоли мы увидим не тело функции, а обьект. И у него есть одно свойство, которое нас интересует. Это свойство [[Scopes]]. Если мы его откроем, то увидим Closure внутри. И внутри Closure у нас и будет наша переменная passed, которую мы передавали как аргумент.

Собственно, Closure в переводе на русский и означает замыкание.

Итак еще раз. Как вы помните все переменные внутри функции создаются при ее вызове и удаляются, после завершения функции.

Но в данном случае мы видим, что функция addTo уже вызвалась, а переменная passed все еще хранится внутри и мы можем ее использовать.

Давайте попробуем вызвать эту функцию еще раз

var addThree = addTo(3)
var addOne = addTo(1)
console.dir(addThree)
console.dir(addOne)

Если мы посмотрим сейчас, то функция вызвалась в обоих случаях и в обоих случаях сохранила внутри себя passed после вызова.

Теперь мы можем этот passed использовать. Собственно он у нас и используется внутри нашей функции add. Но мы эту функцию еще не вызвали. Мы ее только вернули и записали в переменные.

Когда мы ее вызовем сейчас, то у нас будет доступ к переменной passed, так как она сохранилась в замыкании.

var addThree = addTo(3)
var addOne = addTo(1)
console.log(addThree(3))
console.log(addOne(2))

Если мы посмотрим в браузер, то у нас вывелось 6 и 3 соответственно.

Итак давайте подытожим, что такое замыкания.

  1. Это внутренная функция, со всеми внешними переменныеми, которые ей доступны.
  2. Замыкания служат для одной единственной цели — сохранить какие-то переменные, после вызова функции.

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

Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.

Введение в замыкание. Closure | Frontend Stuff

Замыкание

Замыкание обеспечивает доступ к переменным в своей лексической области; включая переменные родителей, которые были удалены из стека вызовов, путём определения, какие именно переменные понадобятся дочерним функциям, путём сохранения их в памяти.

Другими словами, замыкание даёт нам доступ к области видимости внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда во время создания функции, внутри неё создаётся ещё одна функция.

Инкапсуляция

Инкапсуляция позволяет нам скрывать/показывать свойства функций и объектов.

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

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

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

Например, в приведенном ниже примере, мы не хотим показывать функцию launch для её вызова, а также не даём доступ к timeWithoutDesctruction:

const makeNuclearButton = () => {
  
  
  

  let timeWithoutDesctruction = 0;
  const passTime = () => timeWithoutDesctruction++;

  
  
  const totalPeaceTime = () => timeWithoutDesctruction;

  const launch = () => {
    timeWithoutDesctruction = -1;
    return '💥'
  }

  setInterval(passTime, 1000);
  return {totalPeaceTime};
}

const button = makeNuclearButton();
button 
button.totalPeaceTime(); 

Таким образом, мы скрываем данные объекта, которые не должны быть напрямую доступны. Вместо прямого доступа к данным нужно вызывать методы.

Эффективное использование памяти

Замыкания эффективны с точки зрения памяти. Используя замыкания, мы можем создать переменные, которые будут храниться в памяти и использоваться в будущем.

const closureTest1 = function() {
  const bigArray = new Array(7000).fill('1');
  console.log('created');

  return function(index) {
    return bigArray[index];
  }
}

const closureTest1Fn = closureTest1();
closureTest1Fn(500);
closureTest1Fn(300);
closureTest1Fn(100);

created 

Или


const closureTest2 = (function() {
  const bigArray = new Array(7000).fill('1');
  console.log('created');

  return function(index) {
    return bigArray[index];
  }
})();


closureTest2(500);
closureTest2(300);
closureTest2(100);

created 

Мы вызываем функцию closureTest1Fn и closureTest2 3 раза, но console.log выводится только один раз. Это происходит потому, что мы, благодаря замыканию, сохраняем в памяти значения bigArray и console.log.

Типичный случай без замыкания приведет к неэффективному использованию памяти: переменная bigArray будет создаваться и сохраняться каждый раз.

function closureTest(index) {
  const bigArray = new Array(7000).fill('1');
  console.log('created');

  return bigArray[index];
};


closureTest(500);
closureTest(300);
closureTest(100);

created 
created 
created 

В функциональном программировании замыкания часто используются для частичного применения и каррирования.

О замыкании—Справка | ArcGIS for Desktop

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

Все настройки, необходимые при работе с замыканием, расположены на панели инструментов Замыкание. Замыкание не ограничено только процессом редактирования, и может быть использовано в других задачах ArcGIS, таких как пространственная привязка и инструмент Измерить (Measure) на панели инструментов Инструменты (Tools). По умолчанию, замыкание включено, а активными типами замыкания являются точки, конечные точки, вершины и ребра. Вы можете включать и выключать отдельные типы замыкания на панели инструментов Замыкание. Чтобы полностью отключить замыкание, щелкните меню Замыкание и снимите отметку рядом с Использовать замыкание.

Управление замыканием производится на уровне карты, поэтому, если тип замыкания включен, можно произвести замыкание на любой видимый слой пространственных объектов. Сюда также входят типы слоев, не доступные для редактирования, например, слой базовой карты или файлы системы автоматизированного проектирования (САПР). Замыкание на скрытые объекты карты произвести нельзя, включая те случаи, когда слой выключен, содержит определяющий запрос (на видимые объекты замыкание производить можно) или выходит за пределы диапазона масштабов. Поскольку можно произвести замыкание на любые видимые объекты слоя, на разработку карты может потребоваться некоторое время. Если вы обнаружите, что произвели замыкание на слои, которые не собирались замыкать, сначала убедитесь, что этот слой нужно отображать. Чтобы использовать панель инструментов Замыкание (Snapping) максимально эффективно, отключите ненужные слои и типы замыкания, установите диапазоны масштабов слоев и следите, чтобы подписи и символы были максимально описательными.

При перемещении курсора, будут появляться подсказки в виде всплывающих Подсказок замыкания (SnapTips) и значка курсора, которые сообщат, на какой слой происходит замыкание и с помощью какого типа замыкания это делается. Каждый тип замыкания (вершина, ребро, конечная точка, пересечение и т.п.) имеет собственное обозначение, которое соответствует иконкам на панели инструментов Замыкание. Например, курсор становится квадратным, когда выполняется замыкание на вершину или прямоугольником с диагональными линиями, когда выполняется замыкание на ребро. По внешнему виду курсора и всплывающим текстовым подсказкам замыкания можно сразу определить слой, на который выполняется замыкание, и тип используемого замыкания.

Например, при работе с улицами и участками, можно видеть Подсказки замыкания (SnapTips) с именем слоя и типом замыкания. При создании новой дороги, замкните ее на конечную точку (Streets: Конечная точка), чтобы присоединить сегмент. Если нужно построить контур по границам участка, производите замыкание на Parcels: Ребро.

Чтобы помочь в определении типа замыкания на панели инструментов Замыкание, при наведении указателя появляются всплывающие подсказки, а также описание на панели статуса в нижней части окна приложения. Дополнительно вы можете изменить внешний вид кнопок на панели инструментов, чтобы кнопки отображали иконки с описанием, или только названия типов замыкания без иконок. Чтобы выполнить эту настройку, щелкните на меню Настроить (Customize) и выберите Режим настройки (Customize mode). Когда появится диалоговое окно Настроить (Customize), щелкните правой кнопкой мыши на иконке на панели инструментов Замыкание (Snapping) и выберите в контекстном меню одну из опций: Только текст (Text Only), Только изображение (Image Only) (по умолчанию), Изображение и текст (Text and Image). Эта настройка применима к любой панели инструментов в ArcGIS.

Чтобы задать опции для работы с замыканием, щелкните меню Замыкание (Snapping) и выберите Опции (Options). Здесь вы можете задать допуск замыкания в пикселях, т. е. расстояние, на котором должен находиться курсор от объекта, чтобы произошло замыкание, или настроить обратную связь замыкания. Также можно изменить цвет значка и содержимое, шрифт и цвет Подсказок замыкания (SnapTips). При работе со снимками, добавьте в Подсказки замыкания (SnapTips) фон, сплошная заливка позади текста облегчит его чтение. Настройки замыкания применяются ко всем сеансам ArcMap, поскольку они сохраняются в реестре приложения.

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

Замыкание при редактировании

Замыкание может быть полезно при многих операциях редактирования, таких как создание полигонов, которые не должны перекрываться или иметь промежутки, при рисовании линий, которые должны пристыковываться друг к другу, или при размещении точек, которые должны быть расположены строго вдоль существующих линий. Например, представьте, что вы создаете новый сегмент электрической сети, которая начинается от существующего трансформатора; необходимо убедиться, что вершина начала линии будет точно соединена с трансформатором. Замыкание позволяет выполнить эту операцию просто и быстро. Вы можете выполнять замыкание на любых слоях объектов на карте, а не только на редактируемых слоях. Это позволяет выполнять замыкание на объектах в чертеже, выполненном в САПР, покрытии, классе пространственных объектов из другой базы геоданных и т. д.

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

В меню Замыкание (Snapping) можно включить замыкание на пересечение, середину сегмента или точку начала кривой. Замыкание на пересечение позволяет производить замыкание на местоположениях, в которых пересекаются два объекта, а вершины или конечные точки могут отсутствовать. Замыкание на пересечение можно использовать при добавлении точек на пересечения улиц или при разделении объекта в месте, где он встречается с другим объектом. Если необходимо разделить парк в том месте, где его пересекает река, нужно включить замыкание на пересечение, затем выбрать полигон парка, щелкнуть инструмент Разрезать полигоны (Cut Polygons) на панели инструментов Редактор (Editor), произвести замыкание на первом пересечении и воспользоваться инструментом Трассировка (Trace), чтобы проследовать вдоль ребра реки. После того как трассировка вдоль парка произведена, необходимо выполнить замыкание на другое пересечение и завершить скетч, чтобы разрезать парк на два объекта.

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

Выбор среды замыкания при редактировании

При редактировании можно выбрать одну из двух сред замыкания, которые доступны в ArcGIS. По умолчанию используется панель инструментов Замыкание (Snapping), но можно включить классическое замыкание для расширенного управления параметрами замыкания.

Параметры классического замыкания в среде редактирования относятся к функциональности замыкания, которая была доступна в сеансе редактирования в ArcGIS 9 и ранее с использованием окна Параметры замыкания (Snapping Environment). В классическом замыкании параметры замыкания указываются в окне Параметры замыкания (Snapping Environment), в котором можно управлять типами, слоями и приоритетами замыкания. Замыкание не будет производиться до тех пор, пока в некоторых полях окна не будут поставлены флажки. Можно перетаскивать слои вверх и вниз списка, чтобы изменить порядок замыкания. Замыкание на слои наверху списка будет производиться в первую очередь. Чтобы произвести замыкание на точки, необходимо включить опцию Вершина (Vertex), поскольку в классическом замыкании нет отдельного типа замыкания на точки. По умолчанию, функциональные возможности классического замыкания отключены и заменены панелью инструментов Замыкание (Snapping), которая предлагает гибкие, легкие в использовании параметры среды замыкания с большим количеством типов замыкания, опций и лучшей обратной связью, по сравнению с классическим замыканием. Поскольку могут возникать ситуации, когда требуется использовать именно классическое замыкание, имеется возможность переключения.

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

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

Каждая среда замыкания имеет свое соотношение простоты использования и уровня контроля. К панели инструментов Замыкание (Snapping) нужно привыкнуть, но она проста в обращении, а ее возможностей будет достаточно для выполнения большинства задач редактирования. Сменить среду замыкания можно в любой момент в диалоговом окне Опции редактирования (Editing Options), поэтому всегда можно выбрать именно ту среду, которая наиболее соответствует текущей задаче.

Примечание:

Когда активно классическое замыкание (clasical snapping), инструменты редактирования используют только этот тип замыкания. Однако инструменты пространственной привязки, Инструмент Измерить и некоторые другие инструменты не для редактирования продолжают применять настройки на панели инструментов Замыкание.

Связанные темы

Отзыв по этому разделу?

замыканий — JavaScript | MDN

Замыкание — это комбинация функции, объединенной (заключенной) со ссылками на ее окружающее состояние (лексическая среда ). Другими словами, замыкание дает вам доступ к области внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда создается функция, во время создания функции.

Рассмотрим следующий пример кода:

  function init () {
  var name = 'Mozilla';
  function displayName () {
    оповещение (имя);
  }
  отображаемое имя();
}
в этом();  

init () создает локальную переменную с именем name и функцию с именем displayName () .Функция displayName () — это внутренняя функция, которая определена внутри init () и доступна только в теле функции init () . Обратите внимание, что функция displayName () не имеет собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций, displayName () может получить доступ к переменной name , объявленной в родительской функции, init () .

Запустите код, используя эту ссылку JSFiddle, и обратите внимание, что оператор alert () в функции displayName () успешно отображает значение переменной name , которая объявлена ​​в ее родительской функции.Это пример лексического области видимости , который описывает, как синтаксический анализатор разрешает имена переменных, когда функции вложены. Слово лексический относится к тому факту, что лексическая область видимости использует место, где объявлена ​​переменная в исходном коде, чтобы определить, где эта переменная доступна. Вложенные функции имеют доступ к переменным, объявленным во внешней области видимости.

Рассмотрим следующий пример кода:

  function makeFunc () {
  var name = 'Mozilla';
  function displayName () {
    оповещение (имя);
  }
  return displayName;
}

var myFunc = makeFunc ();
myFunc ();
  

Выполнение этого кода имеет тот же эффект, что и предыдущий пример функции init () выше.Что отличается (и интересно), так это то, что внутренняя функция displayName () возвращается из внешней функции перед выполнением .

На первый взгляд может показаться нелогичным, что этот код все еще работает. В некоторых языках программирования локальные переменные внутри функции существуют только на время выполнения этой функции. После завершения выполнения makeFunc () можно ожидать, что переменная name больше не будет доступна. Однако, поскольку код по-прежнему работает должным образом, в JavaScript это явно не так.

Причина в том, что функции в JavaScript закрывают формы. Замыкание — это комбинация функции и лексической среды, в которой эта функция была объявлена. Эта среда состоит из любых локальных переменных, которые были в области видимости во время создания замыкания. В этом случае myFunc — это ссылка на экземпляр функции displayName , которая создается при запуске makeFunc . Экземпляр displayName поддерживает ссылку на свою лексическую среду, в которой существует переменная name .По этой причине, когда вызывается myFunc , переменная name остается доступной для использования, а «Mozilla» передается в alert .

Вот немного более интересный пример — функция makeAdder :

  function makeAdder (x) {
  return function (y) {
    вернуть x + y;
  };
}

var add5 = makeAdder (5);
var add10 = makeAdder (10);

console.log (add5 (2));
console.log (add10 (2));
  

В этом примере мы определили функцию makeAdder (x) , которая принимает единственный аргумент x и возвращает новую функцию.Возвращаемая функция принимает единственный аргумент y и возвращает сумму x и y .

По сути, makeAdder — это фабрика функций. Он создает функции, которые могут добавлять определенное значение к своему аргументу. В приведенном выше примере фабрика функций создает две новые функции: одна добавляет пять к своему аргументу, а вторая добавляет 10.

add5 и add10 — оба закрытия. Они используют одно и то же определение тела функции, но хранят разные лексические среды.В лексической среде add5 x равно 5, а в лексической среде для add10 x равно 10.

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

Следовательно, вы можете использовать замыкание везде, где вы обычно можете использовать объект только с одним методом.

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

Например, предположим, что мы хотим добавить на страницу кнопки для регулировки размера текста. Один из способов сделать это — указать размер шрифта элемента body (в пикселях), а затем установить размер других элементов на странице (например, заголовков), используя относительную единицу em :

  кузов {
  семейство шрифтов: Helvetica, Arial, sans-serif;
  размер шрифта: 12 пикселей;
}

h2 {
  размер шрифта: 1.5em;
}

h3 {
  размер шрифта: 1.2em;
}
  

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

Вот код JavaScript:

  function makeSizer (size) {
  return function () {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer (12);
var size14 = makeSizer (14);
var size16 = makeSizer (16);
  

size12 , size14 и size16 теперь являются функциями, которые изменяют размер основного текста на 12, 14 и 16 пикселей соответственно.Вы можете прикрепить их к кнопкам (в данном случае гиперссылкам), как показано в следующем примере кода.

  document.getElementById ('размер-12'). Onclick = size12;
document.getElementById ('размер-14'). onclick = size14;
document.getElementById ('размер-16'). onclick = size16;
  
   12 
 14 
 16 
  

Запустите код с помощью JSFiddle.

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

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

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

  var counter = (function () {
  var privateCounter = 0;
  function changeBy (val) {
    privateCounter + = val;
  }

  возвращаться {
    инкремент: функция () {
      changeBy (1);
    },

    декремент: function () {
      changeBy (-1);
    },

    value: function () {
      return privateCounter;
    }
  };
}) ();

приставка.журнал (counter.value ());

counter.increment ();
counter.increment ();
console.log (counter.value ());

counter.decrement ();
console.log (counter.value ());
  

В предыдущих примерах каждое замыкание имело свое собственное лексическое окружение. Однако здесь существует единая лексическая среда, которая используется тремя функциями: counter.increment , counter.decrement и counter.value .

Совместно используемая лексическая среда создается в теле анонимной функции , которая выполняется сразу после ее определения (также известной как IIFE).Лексическая среда содержит два закрытых элемента: переменную с именем privateCounter и функцию с именем changeBy . Вы не можете получить доступ ни к одному из этих закрытых членов извне анонимной функции. Вместо этого вы можете получить к ним доступ с помощью трех общедоступных функций, возвращаемых анонимной оболочкой.

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

  var makeCounter = function () {
  var privateCounter = 0;
  function changeBy (val) {
    privateCounter + = val;
  }
  возвращаться {
    инкремент: функция () {
      changeBy (1);
    },

    декремент: function () {
      changeBy (-1);
    },

    value: function () {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter ();
var counter2 = makeCounter ();

предупреждение (counter1.value ());

counter1.increment ();
counter1.increment ();
предупреждение (counter1.value ());

counter1.decrement ();
предупреждение (counter1.значение());
предупреждение (counter2.value ());
  

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

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

Каждое укупорочное средство имеет три области применения:

  • Локальная область действия (Собственная область действия)
  • Объем внешних функций
  • Глобальный охват

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

 
var e = 10;
function sum (a) {
  return function (b) {
    return function (c) {
      
      return function (d) {
        
        вернуть a + b + c + d + e;
      }
    }
  }
}

console.log (сумма (1) (2) (3) (4));




var e = 10;
function sum (a) {
  функция возврата sum2 (b) {
    функция возврата sum3 (c) {
      
      функция возврата sum4 (d) {
        
        вернуть a + b + c + d + e;
      }
    }
  }
}

var s = sum (1);
var s1 = s (2);
var s2 = s1 (3);
var s3 = s2 (4);
console.log (s3)
  

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

До введения ключевого слова let в ECMAScript 2015 общая проблема с замыканиями возникала, когда вы создавали их внутри цикла. Для демонстрации рассмотрим следующий пример кода.

  

Здесь появятся полезные примечания

Электронная почта:

Имя:

Возраст:

  function showHelp (help) {
  документ.getElementById ('помощь'). textContent = help;
}

function setupHelp () {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес электронной почты'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
    ];

  for (var i = 0; i  

Попробуйте запустить код в JSFiddle.

Массив helpText определяет три полезных подсказки, каждая из которых связана с идентификатором поля ввода в документе. Цикл циклически перебирает эти определения, связывая событие onfocus с каждым из них, которое показывает связанный метод справки.

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

Причина этого в том, что функции, назначенные для onfocus , являются закрытием; они состоят из определения функции и захваченного окружения из области действия функции setupHelp .Цикл создал три замыкания, но каждое из них использует одну и ту же лексическую среду, в которой есть переменная с изменяющимися значениями (, элемент ). Это связано с тем, что переменная item объявлена ​​с var и, таким образом, имеет область действия из-за подъема. Значение item.help определяется, когда выполняются обратные вызовы onfocus . Поскольку к тому времени цикл уже завершился, объект переменной item (общий для всех трех замыканий) остался указывающим на последнюю запись в списке helpText .

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

  function showHelp (help) {
  document.getElementById ('справка'). textContent = help;
}

function makeHelpCallback (help) {
  return function () {
    showHelp (помощь);
  };
}

function setupHelp () {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес электронной почты'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
    ];

  for (var i = 0; i  

Запустите код, используя эту ссылку JSFiddle.

Работает, как ожидалось. Вместо того, чтобы все обратные вызовы совместно использовали единую лексическую среду, функция makeHelpCallback создает новую лексическую среду для каждого обратного вызова, в которой help ссылается на соответствующую строку из массива helpText .

Еще один способ записать вышеизложенное с использованием анонимных замыканий:

  function showHelp (help) {
  document.getElementById ('справка'). textContent = help;
}

function setupHelp () {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес электронной почты'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
    ];

  for (var i = 0; i  

Если вы не хотите использовать больше замыканий, вы можете использовать ключевое слово let , представленное в ES2015:

  function showHelp (help) {
  document.getElementById ('справка'). textContent = help;
}

function setupHelp () {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес электронной почты'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
    ];

  for (пусть i = 0; i  

В этом примере используется let вместо var , поэтому каждое замыкание связывает переменную с областью действия блока, что означает, что дополнительных замыканий не требуется.

Другой альтернативой может быть использование forEach () для перебора массива helpText и присоединения слушателя к каждому , как показано:

  function showHelp (help) {
  документ.getElementById ('помощь'). textContent = help;
}

function setupHelp () {
  var helpText = [
      {'id': 'email', 'help': 'Ваш адрес электронной почты'},
      {'id': 'name', 'help': 'Ваше полное имя'},
      {'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
    ];

  helpText.forEach (function (text) {
    document.getElementById (text.id) .onfocus = function () {
      showHelp (text.help);
    }
  });
}

setupHelp ();  

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

Например, при создании нового объекта / класса методы обычно должны быть связаны с прототипом объекта, а не определены в конструкторе объекта. Причина в том, что всякий раз, когда вызывается конструктор, методы будут переназначены (то есть для каждого создания объекта).

Рассмотрим следующий случай:

  function MyObject (имя, сообщение) {
  this.name = name.toString ();
  this.message = message.toString ();
  this.getName = function () {
    верни это.название;
  };

  this.getMessage = function () {
    вернуть this.message;
  };
}
  

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

  function MyObject (имя, сообщение) {
  this.name = name.toString ();
  this.message = message.toString ();
}
MyObject.prototype = {
  getName: function () {
    вернуть this.name;
  },
  getMessage: function () {
    верни это.сообщение;
  }
};
  

Однако переопределять прототип не рекомендуется. Следующий пример вместо этого добавляется к существующему прототипу:

  function MyObject (имя, сообщение) {
  this.name = name.toString ();
  this.message = message.toString ();
}
MyObject.prototype.getName = function () {
  вернуть this.name;
};
MyObject.prototype.getMessage = function () {
  вернуть this.message;
};
  

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

Определение закрытия по Merriam-Webster

закрыто | \ ˈKlō-zhər \

1 : акт закрытия : условие закрытия закрытие век закрытие бизнеса закрытие фабрики

2 : часто успокаивающее или удовлетворяющее чувство завершенности жертвы, нуждающиеся в закрытии также : что-то (например, удовлетворительный финал), дающее такое ощущение

3 : то, что закрывает карман на молнии с защитой от детей

4 [перевод французского clôture ] : закрытие

5 : свойство, которое имеет система счисления или множество, когда они математически закрываются при выполнении операции.

6 : набор, который состоит из данного набора вместе со всеми предельными точками этого набора.

USPS начинает новый раунд закрытия объектов, а также закупает еще несколько десятков

The U.S. Postal Service объединит 18 своих почтовых предприятий, объединив это объявление во вторник с планами закупить 45 новых дополнительных объектов, которые увеличат возможности агентства по доставке посылок.

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

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

Генеральный почтмейстер Луи ДеДжой недавно представил шаги в своем 10-летнем бизнес-плане, который, по его словам, позволит USPS выйти на уровень безубыточности и предотвратить убытки в размере 160 миллиардов долларов в течение следующего десятилетия.USPS обязалась вложить 40 миллиардов долларов в капитальные вложения в рамках этого плана, чтобы сопровождать более медленные стандарты доставки для некоторых почтовых отправлений, сокращение часов в некоторых почтовых отделениях и закрытие заводов.

В дополнение к недавно объявленному контракту на начало замены устаревшего автопарка, первая часть этих инвестиций пойдет на дополнения и «ускоренную» закупку 138 сортировщиков упаковки. Почтовая служба ожидает, что машины и оборудование будут введены в эксплуатацию в преддверии праздничного сезона 2021 года предстоящей зимой, что является самым загруженным периодом для агентства каждый год.На фоне пандемии и соответствующей нехватки персонала, юридических баталий по поводу операционных изменений, которые ДеДжой пытался реализовать вскоре после вступления в должность, и беспрецедентного объема, клиенты USPS столкнулись со значительными задержками в конце 2020 года, которые перевернули операции и вызвали широкий протест.

Объем упаковки вырос на 28% за последний год, заявили в USPS, и новое оборудование позволит более плавно обрабатывать данные для удовлетворения растущего спроса в электронной коммерции. Между тем, за последнее десятилетие почтовые отправления первого класса упали на 23%.45 арендованных дополнительных объектов будут расположены рядом с существующими перерабатывающими заводами и позволят USPS лучше справляться с скачками и переполнением, пояснили в агентстве.

USPS продвигается вперед в консолидации своих объектов, несмотря на отчет генерального инспектора за 2018 год, согласно которому USPS реализовало всего 5% из 1,6 миллиарда долларов экономии, которую он спрогнозировал в результате консолидаций. Агентство успешно закрыло 141 завод на первом этапе своего плана, но отключило второй этап, чтобы закрыть еще 82 завода, когда он был на полпути.Законодатели в то время умоляли почтовую администрацию приостановить действие плана, что потребовало бы тысяч рабочих мест и дальнейшего снижения стандартов доставки, и USPS в конечном итоге согласился с неуместной надеждой, что это приведет к законодательному пересмотру почты.

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

«[Для] перемещения почты потребуется, чтобы мы выделили больше персонала для поддержки обработки посылок с учетом увеличения объема посылок и выделим меньше сотрудников для обработки почты с учетом значительного сокращения почтовых отправлений», - сказал Фрум.

ДеДжой сообщил Правительству в прошлом месяце, что консолидация будет «минимальной» и «только для доработки», в то время как некоторые объекты будут перепрофилированы.Как и в случае с небольшим раундом консолидаций на этот раз, сокращение физического объема в начале 2010-х годов - тогда известное как «рационализация сети» - сопровождалось более медленными окнами доставки почты. Согласно текущему плану, только около 40% почты первого класса будут затронуты. Больше почты будет доставлено в течение трех-пяти дней, поскольку USPS стремится значительно снизить свою зависимость от авиаперевозок.

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

Он предсказал, что сочетание добавлений и вычитаний средств позволит USPS более регулярно выполнять свои окна доставки. Агентство доставило менее 88% почты, предназначенной для доставки от трех до пяти дней, вовремя в 2020 финансовом году, что более чем на 7 процентных пунктов ниже запланированного.

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

После обнародования план ДеДжоя получил быстрое сопротивление со стороны некоторых законодателей-демократов и неоднозначные отзывы от почтовых рабочих групп. Марк Димондстайн, президент Американского союза почтовых рабочих, особо раскритиковал закрытие заводов, заявив, что необходимо будет предпринять любые попытки «продолжить провальную стратегию консолидации заводов».

В том, что также обещает оказаться спорным, USPS объявила, что возобновит перемещение или удаление «ненужного» оборудования для сортировки почты, чтобы освободить место для дополнительных сортировщиков пакетов.Почтовая служба попыталась отклонить любую критику, отметив, что перемещение и удаление почтового оборудования является постоянной стратегией, «уходящей корнями в прошлое», но эти усилия в прошлом году оказались в центре внимания в преддверии выборов. ДеДжой в конечном итоге пообещал приостановить инициативу до завершения выборов и позже столкнулся с судебным приказом сделать то же самое, хотя и дал понять, что не будет заменять уже отключенные машины и не планирует отказываться от этой стратегии в долгосрочной перспективе.

«Не секрет, что Почтовая служба испытывает финансовые трудности, - сказал Фрум.«Инвестиции в почтовые перевозки - это часть 10-летнего плана по достижению финансовой устойчивости и высокого качества обслуживания. Приобретение надлежащего оборудования и оборудования для обработки упаковки сейчас поможет удовлетворить растущие потребности американских государственных и деловых клиентов в сфере почтовых и транспортных услуг в преддверии праздничного сезона 2021 года ».

Более постоянное закрытие предприятий округа Лейн в результате воздействия пандемии

Пандемия COVID-19 продолжает оказывать влияние на местную экономику: все больше предприятий постоянно закрываются в округе Лейн, при этом основная часть сосредоточена в Юджин-Спрингфилде.

Около 40 предприятий в Юджин-Спрингфилде были обнаружены навсегда закрытыми в результате исследования Register-Guard, но прилегающие районы, такие как Коттедж-Гроув и Флоренция, сообщают о меньших убытках и даже некоторых экономических выгодах.

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

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

Предыдущее покрытие: Более 25 ресторанов и предприятий Юджина, Спрингфилда и предприятий, которые навсегда закрылись во время пандемии

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

Uki Uki Sushi Bar и Tiki Lounge открылись в январе 2019 года на Бродвей-стрит в центре Юджина. Владельцы мужа и жены Майкл Зито и Софи Диксон принесли Юджину его единственный тики-бар с широким выбором суши и коктейли в модной атмосфере, ориентированной на растения.

Владельцы Uki Uki разместили уведомление о закрытии в Instagram 11 февраля:

«К сожалению, нам пришлось навсегда закрыть Uki Uki.Мы боролись за это, но доступные нам государственные программы не имели большого значения, и то же самое можно сказать обо всех небольших независимых ресторанах. Это, и наши арендодатели действительно хотели, чтобы мы платили полную арендную плату за пространство, которое мы не могли должным образом использовать ».

Суши-бар не одинок в своем закрытии.

Регистр-гвардия выявила 32 постоянных закрытия предприятий в Юджин один с марта 2021 года. В Спрингфилде зафиксировано шесть закрытий и, конечно же, другие, которые еще предстоит найти.

Сообщества округа Лейн

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

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

«Девятьсот из 4600 рабочих мест во Флоренции связаны с туризмом, - пояснил Ханниган.

«Сообщество из 15 000 человек не сможет поддерживать (местную ресторанную) индустрию без туризма. Это делает нас очень уязвимыми, если люди не путешествуют».

К счастью, во время пандемии туризм действительно резко вырос во время пандемии в западном округе Лейн, в таких городах, как Флоренция, Мейплтон и Дюнс-Сити, сказал Ханниган. Она размышляла о том, что обилие домов на колесах и аналогичных кемпингов, доступных на побережье, были важным активом в поддержании бизнеса, поскольку люди путешествуют «более безопасными» методами.

ICYMI: Округ Лейн может вернуться к «экстремальному риску» 30 апреля, закрыв закрытые столовые

Кроме того, 26 марта округ Лейн перешел на уровень «низкого риска», увеличив количество ресторанов и развлечений, отдыха в помещении и розничных магазинов вместимость.

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

Это предприятия, которые были окончательно закрыты во время пандемии в Юджине и Спрингфилде, как показали исследования Register-Guard.Было обнаружено, что многие предприятия были временно закрыты из-за пандемии. Это не полный список, и он будет обновляться.

Закрытие в Юджин-Спрингфилде

Bijou Art Cinema

Независимый художественный театр на 13-й авеню, где с 1981 года показывают фильмы по адресу 492 E. 13th Ave.

Подробнее: Bijou COVID оспаривается закрытие

Mi

Вьетнамский ресторан рядом с Университетом Орегона по адресу 849 E.13th Ave.

Театр Дэвида Минора

Владельцы первого кинотеатра и паба Юджина на Пятой авеню, 180 заявили, что они перешли на работу над своим велосипедным бизнесом 360 Cycles, согласно объявлению о закрытии кинотеатра в Facebook. .

Epic Elixirz

Магазин шоколада и кафе с шоколадным напитком и другими деликатесами на 960 W. Fifth Ave.

Eugene Tour and Travel

Туристическое агентство с полным спектром услуг по адресу 1142 Willagillespie Road, # 24.

Gather Together Yoga

Студия йоги, увлеченная инклюзивностью и вдохновением, на 2620 River Road, Suite A.

Midtown Pilates

Студия пилатеса, предлагающая различные варианты упражнений в центре города по адресу 1680 Willamette St ., # 4046.

(История продолжается ниже)

Uki Uki

Uki Uki превратился в модный суши-ресторан с разделенными тарелками, украшенный атмосферой тики-бара по адресу 901 Pearl St.

Академия гимнастики США

Местная академия гимнастики, обслуживающая детей и их семьи с 2005 года по адресу 4540 Commerce St.

Вилья-де-ла-Пас

Мексиканский ресторан в бизнес-центре Center West на 2190 W. 11th Ave.

Наша швейная комната

Швейная комната для друзей, предлагающая с 2009 года швейные машины, сергеры, раскройные столы и зоны для глажки в центре Спрингфилда по адресу 448 Main St.

Знаете ли вы о предприятии, которое окончательно закрылось во время пандемии, не указанном здесь? Напишите dsparks @ registerguard.com с названием компании, местонахождением и контактной информацией, которую вы можете указать.

Свяжитесь с репортером Даной Спаркс по телефону [email protected] или 541-338-2243 и подпишитесь на нее в Twitter @danamsparks и Instagram @danasparksphoto .

перекрытия переулков на следующей неделе в Арлингтоне, Берлесоне, Форт-Уэрте, Халтом-Сити, Саутлейке - NBC 5 Даллас-Форт-Уэрт

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

Для обеспечения наилучшего передвижения настоятельно рекомендуется использовать альтернативные маршруты.

Арлингтон

Правая полоса SH 121, идущая на восток, будет закрыта, начиная с Handley Ederville Road до SH 10, в связи со строительством с 9:00 до 15:00. Понедельник, 26 апреля.

Spur 303 на Pioneer Parkway, правая полоса движения в восточном направлении также будет закрыта, начиная с Browning Drive до Carter Drive в связи со строительством с 9:00 до 15:00. с понедельника по пятницу, 26-30 апреля.

Бурлесон

Несколько полос будут закрыты в Берлесоне до района Форт-Уэрт, а также на трассе I-35W в северном и южном направлении, начиная с Risinger Rd. до SH 174 в связи со строительством с 20:00 до 5 часов утра с понедельника по пятницу, с 26 по 30 апреля.

Форт-Уэрт

В дополнение к пункту выше, различные полосы движения будут закрыты на SH 170 в восточном и западном направлении от I-35W до US 377 в связи со строительством с 9:00 до 15:00. С понедельника по субботу, с 26 апреля по 1 мая.Эта конструкция также вызовет закрытие различных кроссоверов.

Бизнес 287, или Риверсайд Драйв, правая полоса движения на юг, также будет закрыта, начиная с Роуздейл-стрит и тянясь до Семинари-Драйв, в связи со строительством с 9:00 до 15:00. С понедельника по пятницу, с 26 по 30 апреля.

Халтом-Сити

Несколько переулков на US 377 в восточном и западном направлении, начиная с Beach Street до SH 183, на 28th Street, в связи со строительством с 19:00. до 6 утра с воскресенья по четверг, с 25 по 29 апреля.

Саутлейк

В Саутлейке правая полоса SH 114 в восточном направлении будет закрыта от бульвара Дэвис до бульвара Кирквуд в связи со строительством с 9:00 до 15:00. С понедельника по субботу, с 26 апреля по 1 мая. Это также приведет к закрытию съездов.

В дополнение к вышеупомянутым закрытиям, будут ожидаемые воздействия на движение транспорта на I-30 и SH 360 в связи с дневным закрытием с 9:00 до 15:00. и ночное закрытие с 20:00. до 5 утра

Для получения дополнительной информации щелкните здесь.

Закрытие предприятий

PARD | AustinTexas.gov

ОБНОВЛЕНИЯ

9 апреля 2021

Начиная с воскресенья, 11 апреля 2021 г., бассейн Варфоломея снова откроется для измененных операций. Теперь, когда бассейны Варфоломея и Биг Стейси снова открыты для измененных операций, Спрингвудский бассейн вернется к своим регулярно изменяемым часам.

8 марта 2021 г.

Springwoods Pool, 13320 Lyndhurst St., откроется во вторник, 9 марта, после завершения ремонта после зимнего шторма Ури. Пресс-релиз

24 февраля 2021 г.
Бассейн

Бартон-Спрингс и бассейн Deep Eddy снова откроются в четверг, 25 февраля 2021 года, после закрытия на прошлой неделе из-за чрезвычайной погодной ситуации. Вход в оба бассейна бесплатный до дальнейшего уведомления. Пресс-релиз

23 февраля 2021 г.

Департамент парков и отдыха города Остина (PARD) продолжает оценку ущерба, нанесенного зимним штормом на своих объектах, и планирует необходимый ремонт. Городские поля для гольфа, кладбища, Теннисный центр Южного Остина и Теннисный центр Остина будут вновь открыты в соответствии с прежними правилами работы с COVID-19. Дополнительные объекты PARD, которым разрешено работать в соответствии с действующим Руководством по рискам COVID-19 города Остина, будут вновь открыты, если позволят ресурсы / ремонт и укомплектование персоналом. О дополнительных операциях будет объявлено по мере их появления в сети.

12 февраля 2021 г.

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

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

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

Будьте осторожны при посещении ледяных парков или троп. Зимние условия могут создавать риски поскользнуться, споткнуться и упасть, а скопившийся лед и / или снег могут повредить деревья и большие ветви.

23 декабря 2020

По согласованию с Департаментом общественного здравоохранения Остина и органами общественного здравоохранения в настоящее время открыты следующие удобства: ОТКРЫТО :

  • Бассейн Варфоломея (открыт только для плавания по кругу, вместимость 25% и измененные операции - нет событий любого размера)
  • Водосброс Бартон-Крик (он же Баркинг-Спрингс)
  • Бассейн Бартон-Спрингс (открыт только для плавания на круге, вместимость 25% и измененные операции - нет событий любого размера)
  • Бассейн Big Stacy (открыт только для плавания на кругах, вместимость 25% и измененные операции - нет событий любого размера)
  • Спуск на воду катера
  • Доки для лодок
  • Судоходные концессии
  • Кемпинги в парке Эмма Лонг Метрополитен
  • Кладбища
  • Концессии для стрельбы по глине
  • Бассейн Deep Eddy (открыт только для плавания по кругу, вместимость 25% и измененные операции - нет событий любого размера)
  • Поля для диск-гольфа
  • Продовольственные концессии
  • Каюта для девочек-скаутов (открытое пространство с загрузкой 25% и измененными операциями - без мероприятий любого размера)
  • Поля для гольфа увеличивают интервалы старта между группами до 12 минут
    • Все турниры отменены
    • Убрать все столы для пикника в павильонах
    • Исключить места для сидения внутри помещений в концессиях по продаже продуктов питания и напитков
    • Поощрение онлайн-платежей
    • Ограничить доступ к профессиональному магазину только для оплаты; внутри могут находиться не более 3 человек с социальным дистанцированием в любой момент времени
    • Увеличьте расстояние в диапазоне движения до 8 футов.
    • Все кувшины для воды сняты
    • Продолжить процесс дезинфекции тележек для гольфа (до и после использования) с помощью одной тележки или только для ходьбы
  • Mayfield Park (только на открытых площадках)
  • Окрестные теннисные корты (и открытые площадки для пиклбола)
  • Уличные тренажеры
  • Туалеты на открытом воздухе
  • Парки Гринспейс
  • Консервы
  • Бассейн Спрингвудс (открыт только для плавания на кругах, вместимость 25% и измененные операции - нет событий любого размера)
  • Sunshine Camp (открытое пространство с загрузкой 25% и измененные операции - без мероприятий любого размера)
  • Теннисные центры с уменьшенной пропускной способностью и измененными операциями (оплата онлайн, ограниченный доступ к туалетам, увеличенное время между бронированием площадок - без мероприятий любого размера)
  • Трассы
  • Сад скульптур Умлауф (открытое пространство только на 25% вместимость и установленные модифицированные операции - никаких мероприятий нет)
  • Фонтаны воды
  • Ботанический сад Цилкер (открытое пространство только при 25% -ной загрузке и измененные операции - без мероприятий любого размера)

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

Согласно декларации мэра COVID-19, следующие объекты PARD ЗАКРЫТЫ для общественности:

  • Все базы отдыха
  • Все объекты для проведения специальных мероприятий
  • Все брызговики
  • Центр природы и науки Остина
  • Кабина для девочек-скаутов в помещении
  • Музейно-культурные учреждения
  • Часовня кладбища Оквуд
  • Центры для пожилых людей
  • Внутреннее пространство Sunshine Camp
  • Дом смотрителя Зилкер
  • Удобства парка, включая, помимо прочего: баскетбольные площадки, скейт-парки, волейбольные площадки, спортивные площадки и павильоны.

Программы ПРИОСТАНОВЛЕНЫ

  • Все атлетические / спортивные программы и лиги
  • Волонтерские программы через систему парков
  • Отсутствие разрешения на проведение мероприятий для использования парка
  • Без павильона / разрешения на пикник
  • Приостановление действия разрешения на съемку фильмов
26 сентября 2020

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



Восстановление измененных операций было выполнено после тщательного рассмотрения местных характеристик вируса и соответствующих приоритетов сообщества.
Было определено, что эти повторные открытия могут безопасно проходить в течение Этап 3: COVID-19: Рекомендации по рискам .

PARD в консультации с Департаментом здравоохранения Остина продолжает ежедневно оценивать общесистемные операции. По мере уменьшения стадий риска дальнейшее рассмотрение будет уделяться дополнительным открытиям PARD.Важно отметить, что если условия ухудшатся, Департамент снова закроет объекты / объекты.

Для получения дополнительной информации об этих измененных открытиях посетите страницу : FAQ по повторному открытию пула .

По согласованию с Департаментом общественного здравоохранения Остина и органами общественного здравоохранения в настоящее время открыты следующие удобства: ОТКРЫТО :

  • Водосброс Бартон-Крик (он же Баркинг-Спрингс)
  • Бассейн Варфоломея
  • Бассейн Бартон-Спрингс
  • Бассейн Большой Стейси
  • Баскетбольные площадки
  • Спуск на воду катера
  • Доки для лодок
  • Судоходные концессии
  • Кемпинги в парке Эмма Лонг Метрополитен
  • Кладбища
  • Концессии для стрельбы по глине
  • Бассейн Deep Eddy
  • Поля для диск-гольфа
  • Event Buildings (отдельные сайты) (резервирование онлайн с ограничениями)
  • Разрешение на съемку (на объектах, где не работают закрытые территории или рабочие ограничения)
  • Продовольственные концессии
  • Каюта для девочек-скаутов (открытое пространство с загрузкой 25% и измененными операциями - без мероприятий любого размера)
  • Поля для гольфа увеличивают интервалы старта между группами до 12 минут
    • Все турниры отменены
    • Убрать все столы для пикника в павильонах
    • Исключить места для сидения внутри помещений в концессиях по продаже продуктов питания и напитков
    • Поощрение онлайн-платежей
    • Ограничить доступ к профессиональному магазину только для оплаты; внутри могут находиться не более 3 человек с социальным дистанцированием в любой момент времени
    • Увеличьте расстояние в диапазоне движения до 8 футов.
    • Все кувшины для воды сняты
    • Продолжить процесс дезинфекции тележек для гольфа (до и после использования) с помощью одной тележки или только для ходьбы
  • Mayfield Park (только на открытых площадках)
  • Окрестные теннисные корты (и открытые площадки для пиклбола)
  • Уличные тренажеры
  • Туалеты на открытом воздухе
  • Парки Гринспейс
  • Разрешение на места для пикника и павильон (резервирование онлайн с ограничениями)
  • Детские площадки
  • Консервы
  • Скейт-парки
  • Площадки для особых мероприятий (с ограничениями и утвержденными планами безопасности здоровья)
  • Пул Спрингвудс
  • Sunshine Camp (открытое пространство с загрузкой 25% и измененные операции - без мероприятий любого размера)
  • Теннисные центры с уменьшенной пропускной способностью и измененными операциями (оплата онлайн, ограниченный доступ к туалетам, увеличенное время между бронированием площадок - без мероприятий любого размера)
  • Трассы
  • Сад скульптур Умлауф (открытое пространство только на 25% вместимость и установленные модифицированные операции - никаких мероприятий нет)
  • Волейбольные площадки
  • Фонтаны воды
  • Ботанический сад Цилкер

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

Следующие объекты PARD ЗАКРЫТЫ для общественности:

  • Все базы отдыха
  • Все брызговики
  • Центр природы и науки Остина
  • Кабина для девочек-скаутов в помещении
  • Музейно-культурные учреждения
  • Часовня кладбища Оквуд
  • Центры для пожилых людей
  • Внутреннее пространство Sunshine Camp
  • Дом смотрителя Зилкер
  • Удобства в парке, в том числе спортивные площадки и павильоны.

Программы ПРИОСТАНОВЛЕНЫ

  • Все атлетические / спортивные программы и лиги
  • Волонтерские программы через систему парков
Пропуск на вход в парк для городских парков Эммы Лонг
  • Необходимо предварительное бронирование онлайн (для посещения с пятницы по воскресенье требуется предварительное бронирование)
    • Эмма Лонг Метрополитен Парк
    • Вход не предоставляется без бронирования дневного пропуска
    • Пропуск доступен за 14 дней и не позднее полуночи накануне.Пропуска отправляются по электронной почте и должны быть распечатаны для демонстрации. При заполнении - никаких дополнительных очередей на вход в парк не допускается. Ограничение 2 в день на семью.
    • Руководство по онлайн-бронированию пропусков
    • Лица, у которых НЕТ компьютера, могут обращаться за помощью в обычные рабочие часы 512-974-6797. (ПРИМЕЧАНИЕ - В настоящее время город работает в режиме измененных операций из-за COVID19. Пожалуйста, позвоните, оставьте сообщение для получения помощи.) P

ПРОЕКТЫ ПАРКОВ

Закрытие туалетов
Дата закрытия: 1 марта 2021 г. - TBA
Описание: Перечисленные ниже туалеты в настоящее время закрыты из-за повреждений, нанесенных недавним зимним штормом.

  • Туалет в парке Варфоломейского района
  • Туалет Civitan Park
  • Туалет в парке района Говалле
  • Туалет Heron Creek
  • Полевой туалет Крейга
  • Туалет в парке Маунтин-Вью
  • Туалет Ramsey Neighborhood Park
  • Уборная парка в районе Шайп
  • Фонтаны Вик Матиаса
  • Туалет Vic Mathias Shores
  • Уборная для приземления лодки Уолша

Walnut Creek Metropolitan Park Playground
Дата закрытия: 7 декабря 2020 г. - май 2021 г.
Описание: Игровая площадка находится на реконструкции, чтобы стать ведущей доступной игровой площадкой для детей всех возрастов и способностей в Северном Остине.Дизайн включает петлю для исследования через сенсорный сад, резиновую поверхность безопасности с множеством игровых возможностей на уровне земли и культовую платформу, которая тематически сочетается с природными зонами, окружающими игровую площадку.

Туалеты Little Stacy
Дата закрытия: 26 августа 2020 г. - TBA
Описание: Техническое обслуживание

Туалет Vic Mathias Shores
Дата закрытия: 3 октября 2019 г. - подлежит уточнению
Описание: Техническое обслуживание

Туалет на берегу озера
Дата закрытия: 13 декабря 2019 г. - TBA
Описание: Техническое обслуживание

Парк округа Пис Кингсбери Коммонс
Дата закрытия: 19 февраля 2020 г. - март 2021 г. (ожидается)
Описание: Южные 7 акров парка округа Пис будут закрыты на строительство.Информация о проекте здесь.

Zilker Train
Дата закрытия: 7 мая 2019 г. - TBA
Описание: из-за отрыва части железнодорожного полотна возле его поворота поезд временно закрыт. Фонд Остина Парка вернет поезд в конце лета 2021 года. Для получения дополнительной информации посетите сайт zilkertrain.org.

Пешеходный мост у Роя Г. Герреро Метрополитен-парк на реке Колорадо
Дата закрытия: 28 мая 2015 г. - TBA
Описание: Пешеходный мост закрыт из-за ущерба от наводнения до дальнейшего уведомления.

Техническое обслуживание помещений

Ежегодное техническое обслуживание бассейна Deep Eddy
Дата закрытия: 11 апреля - 2 мая 2021 г.
Описание: Бассейн Deep Eddy, 401 Deep Eddy Ave., будет закрыт на ежегодную генеральную уборку. Бассейн вновь откроется в понедельник, 3 мая, и возобновит работу в измененные обычные часы. Пресс-релиз

ТРОПЫ

Трассы вокруг бассейна Deep Eddy
Дата закрытия: 6 апреля и 9 апреля 2021 года
Описание: Во время ежегодного технического обслуживания бассейна Deep Eddy Pool вход на пешеходную и велосипедную тропу Lady Bird Lake через парк Эйлерс и за бассейном Deep Eddy будет быть заблокированным.

Тропа для прогулок по Шол-Крик (5-6-я улица)
Дата закрытия: 16 ноября 2020 г. - ноябрь 2021 г.
Описание: Пандус к Шол-Крик между улицами 5 и 6 будет закрыт около года. Сама тропа останется открытой, только не съезд на 6 -ю улицу .

Shoal Creek Trail (с 24 по 29 Ламар)
Дата закрытия: 4 мая 2018 г. - TBA
Описание: поврежден оползнем.Департамент водораздела города работает над планами стабилизации склона холма и предоставления нам места для замены тропы. Мы ожидаем окончания строительства в начале следующего года, если все пойдет по плану. Сайт

ИГРОВАЯ ПЛОЩАДКА

Walnut Creek Metropolitan Park Playground
Дата закрытия: 7 декабря 2020 г. - май 2021 г.
Описание: Игровая площадка находится на реконструкции, чтобы стать ведущей доступной игровой площадкой для детей всех возрастов и способностей в Северном Остине.Дизайн включает петлю для исследования через сенсорный сад, резиновую поверхность безопасности с множеством игровых возможностей на уровне земли и культовую платформу, которая тематически сочетается с природными зонами, окружающими игровую площадку.

АТЛЕТИКА И ГОЛЬФ

ТЕХНИЧЕСКОЕ ОБСЛУЖИВАНИЕ ТЕРВА

Great Lawn
Дата закрытия: 27 апреля 2021 г.
Описание: Во вторник, 27 апреля, -е, , входные ворота на Great Lawn в парке Zilker будут закрыты с 6 утра.Персонал будет выполнять техническое обслуживание ирригации, включая промывку 8-дюймовой магистральной оросительной линии на мысе Лу Нефф. Посетители парка могут припарковаться на южной стороне Бартон-Спрингс-роуд и перейти улицу по перекрестку. Они также могут припарковаться на гравийной стоянке возле Stratford Dr, чтобы добраться до Великой лужайки. Входные ворота будут открыты с 9 до 10 утра.

Что такое закрытие? - Calhoun.io

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

«Я кое-что узнал. В основном то, что я понятия не имею, как работают замыкания в Go »

Ой! Это означает, что, несмотря на то, что остальная часть статьи наполнена отличным контентом о тестировании, если читатель не понимает закрытия, он не сможет получить все преимущества статьи.

Этот пост предназначен для решения этой проблемы.В нем мы собираемся обсудить, что такое замыкания и почему вы должны заботиться о них. Затем в следующей статье «5 полезных способов использования замыканий в Go» (избыточных, я знаю) мы рассмотрим некоторые из наиболее распространенных вариантов использования замыканий, чтобы помочь вам понять, где и когда использовать замыкания в вашем собственном коде. У каждого варианта использования есть конкретный пример, с которым я столкнулся, так что это не просто воображаемое использование.

Анонимные функции

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

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

  func DoStuff () {
  // Делаем что-нибудь
}
  

Если бы мы хотели превратить эту же функцию в анонимную, мы бы не объявляли ее таким же образом; вместо того, чтобы начинать с ключевого слова func , мы могли бы вместо этого создать переменную с типом func () .После этого мы могли создать и назначить анонимную функцию переменной.

  var DoStuff func () = func () {
  // Делаем что-нибудь
}
  

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

  пакет основной

импорт "FMT"

var DoStuff func () = func () {
  // Делаем что-нибудь
}

func main () {
  DoStuff ()

  DoStuff = func () {
    fmt.Println ("Делаю что-нибудь!")
  }
  DoStuff ()

  DoStuff = func () {
    fmt.Println ("Занимаюсь другими делами.")
  }
  DoStuff ()
}
  

Если вы запустите эту программу, вы увидите следующий результат.

 Делаем всякие!
Занимаюсь другими делами.
 

Мы видим две разные строки вывода, потому что мы объявили три разные анонимные функции, и каждая из последних двух выводит что-то свое.

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

  пакет основной

импорт "FMT"

var DoStuff func () = func () {
// Делаем что-нибудь
}

func RegFunc () {fmt.Println ("reg func")}

func main () {
DoStuff ()
DoStuff = RegFunc
DoStuff ()
}
  

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

затворы

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

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

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

  пакет основной

импорт "FMT"

func main () {
  п: = 0
  counter: = func () int {
    п + = 1
    вернуть n
  }
  fmt.Println (counter ())
  fmt.Println (counter ())
}
  

Если вы запустите этот код, вы получите результат:

Обратите внимание, как наша анонимная функция имеет доступ к переменной n , но она никогда не передавалась в качестве параметра при вызове counter () .Это то, что делает его закрытием!

Замыкания обеспечивают изоляцию данных

Одна проблема с предыдущим примером - проблема, которая также может всплывать при использовании глобальных переменных. Любой код внутри функции main () имеет доступ к n , поэтому можно увеличивать счетчик без фактического вызова counter () . Мы не этого хотим; Вместо этого мы предпочли бы изолировать n , чтобы никакой другой код не имел к нему доступа.

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

  пакет основной

импорт "FMT"

func main () {
  counter: = newCounter ()
  fmt.Println (counter ())
  fmt.Println (counter ())
}

func newCounter () func () int {
  п: = 0
  return func () int {
    п + = 1
    вернуть n
  }
}
  

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

Хотите улучшить свои навыки игры в го?

Вы хотите попрактиковаться в го, но не можете придумать хороший проект для работы? Или, может быть, вы просто не знаете, какие техники и навыки вам нужно изучить дальше, поэтому не знаете, с чего начать. Если так, не волнуйтесь - я вас прикрыл!

Gophercises - это БЕСПЛАТНЫЙ курс, в котором мы работаем над задачами упражнений, каждая из которых предназначена для того, чтобы научить вас различным аспектам го.Сюда входят темы, начиная от базовых манипуляций со строками и заканчивая более сложными темами, такими как функциональные параметры и параллелизм. В каждом упражнении есть образец решения, а также скринкаст (видео), в котором я кодирую решение, пока вы по коду показываете. К тому же суслики действительно милые 😉

БЕСПЛАТНЫЙ курс Суслики - упражнения для начинающих сусликов

Далее…

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *