Как бы вы объяснили JavaScript-замыкания кому-то, кто знает концепции, из которых они состоят (например, функции, переменные и т. П.), Но не понимает сами замыкания?
Я видел пример схемы, приведенный в Википедии, но, к сожалению, это не помогло.
Как бы вы объяснили JavaScript-замыкания кому-то, кто знает концепции, из которых они состоят (например, функции, переменные и т. П.), Но не понимает сами замыкания?
Я видел пример схемы, приведенный в Википедии, но, к сожалению, это не помогло.
closure
кода, в отличие от того,Object Literal
который повторно использует себя и точно так же снижает накладные расходы, но требует на 100% меньше кода для переноса.Ответы:
Закрытие представляет собой сочетание:
Лексическая среда является частью каждого контекста выполнения (стекового фрейма) и представляет собой карту между идентификаторами (то есть именами локальных переменных) и значениями.
Каждая функция в JavaScript поддерживает ссылку на внешнюю лексическую среду. Эта ссылка используется для настройки контекста выполнения, созданного при вызове функции. Эта ссылка позволяет коду внутри функции «видеть» переменные, объявленные вне функции, независимо от того, когда и где вызывается функция.
Если функция была вызвана функцией, которая в свою очередь была вызвана другой функцией, то создается цепочка ссылок на внешние лексические среды. Эта цепочка называется цепочкой областей действия.
В следующем коде
inner
создается закрытие с лексической средой контекста выполнения, созданной приfoo
вызове, закрывающей переменнуюsecret
:Другими словами: в JavaScript функции содержат ссылку на частный «блок состояния», к которому имеют доступ только они (и любые другие функции, объявленные в той же лексической среде). Этот блок состояния невидим для вызывающей функции, предоставляя отличный механизм для сокрытия данных и инкапсуляции.
И помните: функции в JavaScript могут передаваться как переменные (функции первого класса), что означает, что эти пары функциональности и состояния могут передаваться по вашей программе: подобно тому, как вы можете передавать экземпляр класса в C ++.
Если бы в JavaScript не было замыканий, то между функциями пришлось бы явно передавать больше состояний, что делало списки параметров длиннее и код становился более шумным.
Итак, если вы хотите, чтобы функция всегда имела доступ к частной части состояния, вы можете использовать замыкание.
... и часто мы действительно хотим , чтобы связать состояние с функцией. Например, в Java или C ++, когда вы добавляете частную переменную экземпляра и метод в класс, вы связываете состояние с функциональностью.
В C и большинстве других распространенных языков после возврата функции все локальные переменные больше не доступны, потому что стековый фрейм разрушен. В JavaScript, если вы объявляете функцию в другой функции, то локальные переменные внешней функции могут оставаться доступными после возврата из нее. Таким образом, в приведенном выше коде
secret
остается доступным для объекта функции после тогоinner
, как он был возвращен изfoo
.Использование Закрытий
Замыкания полезны, когда вам нужно личное состояние, связанное с функцией. Это очень распространенный сценарий - и помните: у JavaScript не было синтаксиса классов до 2015 года, и он все еще не имеет синтаксиса закрытых полей. Замыкания удовлетворяют эту потребность.
Переменные частного экземпляра
В следующем коде функция
toString
закрывает детали автомобиля.Функциональное программирование
В следующем коде функция
inner
закрывается какfn
иargs
.Событийно-ориентированное программирование
В следующем коде функция
onClick
закрывается над переменнойBACKGROUND_COLOR
.Модульность
В следующем примере все детали реализации скрыты внутри сразу выполняемого выражения функции. Функции
tick
иtoString
закрытие по частному состоянию и функциям, которые им необходимы для завершения своей работы. Замыкания позволили нам модульно и инкапсулировать наш код.Примеры
Пример 1
Этот пример показывает , что локальные переменные не скопированные в замыкании: замыкание содержит ссылку на исходные переменные сами . Как будто стековый фрейм остается в памяти даже после выхода из внешней функции.
Пример 2
В следующем коде, тремя способами
log
,increment
иupdate
все близкие по одной и той же лексической среде.И каждый раз, когда
createObject
вызывается, создается новый контекст выполнения (стековый фрейм) и создается совершенно новая переменнаяx
, а также новый набор функций (log
и т. Д.), Которые закрываются над этой новой переменной.Пример 3
Если вы используете переменные, объявленные с использованием
var
, будьте осторожны, понимая, какую переменную вы закрываете. Переменные, объявленные с использованиемvar
, поднимаются. Это намного меньше проблем в современном JavaScript из-за введенияlet
иconst
.В следующем коде каждый раз вокруг цикла создается новая функция
inner
, которая закрываетсяi
. Но поскольку онvar i
находится вне цикла, все эти внутренние функции закрываются по одной и той же переменной, а это означает, что окончательное значениеi
(3) печатается три раза.Финальные очки:
function
изнутри другой функции является классическим примером замыкания, поскольку состояние внутри внешней функции неявно доступно для возвращенной внутренней функции даже после того, как внешняя функция завершила выполнение.eval()
внутри функции, используется замыкание. В тексте выeval
можете ссылаться на локальные переменные функции, а в нестрогом режиме вы даже можете создавать новые локальные переменные, используяeval('var foo = …')
.new Function(…)
( конструктор Function ) внутри функции, она не закрывается в своей лексической среде: вместо этого она закрывается в глобальном контексте. Новая функция не может ссылаться на локальные переменные внешней функции.связи
источник
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
от developer.mozilla.org/en-US/docs/Web/JavaScript/Closureslet i = 0
вместоvar i = 0
примера 5, онtestList()
напечатает то, что вы хотели изначально.Каждая функция в JavaScript поддерживает ссылку на внешнюю лексическую среду. Лексическая среда - это карта всех имен (например, переменных, параметров) в области видимости с их значениями.
Поэтому всякий раз, когда вы видите
function
ключевое слово, код внутри этой функции имеет доступ к переменным, объявленным вне функции.Это будет регистрироваться,
16
потому что функцияbar
закрывает параметрx
и переменнуюtmp
, которые существуют в лексическом окружении внешней функции.foo
.Функция
bar
вместе с ее связью с лексическим окружением функцииfoo
является замыканием.Функция не должна возвращаться , чтобы создать замыкание. Просто в силу своего объявления каждая функция закрывается в окружающей лексической среде, образуя замыкание.
Вышеприведенная функция также зарегистрирует 16, потому что код внутри
bar
все еще может ссылаться на аргументx
и переменнуюtmp
, даже если они больше не находятся в области видимости.Тем не менее, так
tmp
как все еще находится внутриbar
замыкания, его можно увеличивать. Он будет увеличиваться каждый раз, когда вы звонитеbar
.Простейший пример замыкания:
Когда вызывается функция JavaScript, создается новый контекст выполнения
ec
. Вместе с аргументами функции и целевым объектом этот контекст выполнения также получает ссылку на лексическую среду вызывающего контекста выполнения, то есть переменные, объявленные во внешней лексической среде (в вышеприведенном примере обаa
иb
) доступны изec
.Каждая функция создает замыкание, потому что каждая функция имеет ссылку на свое внешнее лексическое окружение.
Обратите внимание, что сами переменные видны из замыкания, а не из копий.
источник
delete
терпит неудачу. Тем не менее, лексическая среда, которую функция будет переносить как [[Scope]] (и, в конечном счете, использовать в качестве основы для своей собственной лексической среды при вызове), определяется при выполнении оператора, определяющего функцию. Это означает , что функция является закрытие над содержимым ЦЕЛЫХ исполняющего объема, независимо от того, какие значения он на самом деле относится к и избегает сферы ли. Пожалуйста, посмотрите разделы 13.2 и 10 в спецификацииПРЕДИСЛОВИЕ: этот ответ был написан, когда вопрос был:
Я почти уверен, что был одним из немногих людей, которые пытались ответить на первый вопрос буквально. С тех пор вопрос менялся несколько раз, поэтому мой ответ теперь может показаться невероятно глупым и неуместным. Надеюсь, общая идея этой истории для некоторых остается забавной.
Я большой поклонник аналогий и метафор при объяснении сложных концепций, поэтому позвольте мне попробовать свои силы с историей.
Давным-давно:
Там была принцесса ...
Она жила в чудесном мире, полном приключений. Она встретила своего очаровательного принца, объехала свой мир на единороге, сражалась с драконами, встречала говорящих животных и много других фантастических вещей.
Но ей всегда придется возвращаться в свой скучный мир дел и взрослых.
И она часто рассказывала им о своем последнем удивительном приключении в роли принцессы.
Но все, что они увидят, это маленькая девочка ...
... рассказывать истории о магии и фантазии.
И хотя взрослые знали о настоящих принцессах, они никогда не поверили бы в единорогов или драконов, потому что никогда их не видели. Взрослые говорили, что они существуют только в воображении маленькой девочки.
Но мы знаем настоящую правду; что маленькая девочка с принцессой внутри ...
... действительно принцесса с маленькой девочкой внутри.
источник
story()
функции, которая является единственным интерфейсом, которыйlittleGirl
экземпляр выставляет в мир магии.story
закрытие, но если бы код былvar story = function() {}; return story;
тогда,littleGirl
было бы закрытие. По крайней мере, такое впечатление, которое я получаю от использования MDN «закрытых» методов с замыканиями : «Эти три публичные функции являются замыканиями, которые используют одну и ту же среду».story
это замыкание, ссылающееся на среду, предоставляемую в рамкахprincess
.princess
также является другим подразумеваемым замыканием, т. е.princess
иlittleGirl
разделяют любую ссылку наparents
массив, который будет существовать назад в среде / области действия, гдеlittleGirl
существует иprincess
определено.princess
чем написано. К сожалению, эта история сейчас немного неуместна в этой теме. Первоначально вопрос был о том, чтобы «объяснить закрытие JavaScript 5-летнему»; мой ответ был единственным, который даже пытался это сделать. Я не сомневаюсь, что это с треском провалилось бы, но, по крайней мере, у этого ответа мог быть шанс заинтересовать пятилетнего ребенка.Принимая этот вопрос всерьез, мы должны выяснить, на что способен типичный шестилетний ребенок познавательно, хотя, по общему признанию, тот, кто интересуется JavaScript, не столь типичен.
О развитии детства: от 5 до 7 лет говорится:
Мы можем использовать этот пример для объяснения замыканий следующим образом:
Мы можем закодировать это в JavaScript следующим образом:
Другие пункты, которые объясняют, почему замыкания интересны:
makeKitchen()
вызывается, создается новое замыкание со своим отдельнымtrashBags
.trashBags
Переменная локальна внутри каждой кухне и не доступны снаружи, а внутренняя функция наgetTrashBag
имущество имеет к нему доступ.getTrashBag
функции делает это здесь.источник
Соломенный Человек
Мне нужно знать, сколько раз была нажата кнопка, и что-то делать при каждом третьем нажатии ...
Достаточно очевидное решение
Теперь это сработает, но оно вторгается во внешнюю область, добавляя переменную, единственная цель которой - отслеживать количество. В некоторых ситуациях это будет предпочтительнее, поскольку вашему внешнему приложению может потребоваться доступ к этой информации. Но в этом случае мы меняем только поведение каждого третьего клика, поэтому желательно включить эту функцию в обработчик событий .
Рассмотрим этот вариант
Обратите внимание на несколько вещей здесь.
В приведенном выше примере я использую поведение закрытия JavaScript. Такое поведение позволяет любой функции иметь доступ к области, в которой она была создана, на неопределенный срок. Чтобы применить это на практике, я немедленно вызываю функцию, которая возвращает другую функцию, и поскольку функция, которую я возвращаю, имеет доступ к внутренней переменной count (из-за описанного выше поведения замыкания), это приводит к закрытой области видимости для использования в результате функция ... не так просто? Давай разбавим это ...
Простое однострочное закрытие
Все переменные за пределами возвращаемой функции доступны для возвращаемой функции, но они не доступны напрямую возвращаемому объекту функции ...
Возьми? Таким образом, в нашем основном примере переменная count содержится в замыкании и всегда доступна обработчику событий, поэтому она сохраняет свое состояние от щелчка к щелчку.
Кроме того, это состояние закрытой переменной полностью доступно как для чтения, так и для присваивания его закрытым переменным.
Вот, пожалуйста. теперь вы полностью инкапсулируете это поведение.
Полный пост в блоге (включая вопросы jQuery)
источник
Замыкания трудно объяснить, потому что они используются для того, чтобы заставить поведение работать так, как все интуитивно ожидают, что оно все равно будет работать. Я нахожу лучший способ объяснить их (и способ, которым я узнал, что они делают), это представить ситуацию без них:
Что бы произошло здесь, если бы JavaScript не знал замыканий? Просто замените вызов в последней строке на тело метода (что в основном и делают вызовы функций), и вы получите:
Теперь, где определение
x
? Мы не определили это в текущем объеме. Единственное решение состоит в том, чтобы позволитьplus5
нести его область (или, скорее, область его родителя) вокруг. Этот способx
четко определен и связан со значением 5.источник
TLDR
Замыкание - это связь между функцией и ее внешней лексической (то есть, как написано) средой, так что идентификаторы (переменные, параметры, объявления функций и т. Д.), Определенные в этой среде, видны изнутри функции, независимо от того, когда или из где функция вызывается.
подробности
В терминологии спецификации ECMAScript можно сказать, что замыкание реализовано посредством
[[Environment]]
ссылки на каждый объект-функцию, которая указывает на лексическую среду, в которой определена функция.Когда функция вызывается с помощью внутреннего
[[Call]]
метода, то[[Environment]]
ссылка на функцию-объект копируется в внешней среды ссылкой на записи среды вновь созданного контекста выполнения (кадра стека).В следующем примере функция
f
закрывается над лексической средой глобального контекста выполнения:В следующем примере функция
h
закрывается над лексической средой функцииg
, которая, в свою очередь, закрывается над лексической средой глобального контекста выполнения.Если внутренняя функция возвращается внешней, то внешняя лексическая среда будет сохраняться после возвращения внешней функции. Это потому, что внешняя лексическая среда должна быть доступна, если внутренняя функция в конечном счете вызывается.
В следующем примере функция
j
закрывается над лексическим окружением функцииi
, что означает, что переменнаяx
видна изнутри функцииj
, еще долго после того, как функцияi
завершила выполнение:В заключение, переменные во внешней лексической среде сами по себе доступны, а не копируются.
Цепочка лексических сред, связанная между контекстами выполнения посредством ссылок на внешние среды, образует цепочку областей действия и определяет идентификаторы, видимые из любой данной функции.
Обратите внимание, что в попытке улучшить ясность и точность, этот ответ был существенно изменен по сравнению с оригиналом.
источник
console.log
таким образом. Если кому-то еще интересно, есть еще: developer.mozilla.org/en-US/docs/DOM/…var
).Хорошо, 6-летний поклонник закрытий. Хотите услышать самый простой пример закрытия?
Давайте представим следующую ситуацию: водитель сидит в машине. Эта машина в самолете. Самолет в аэропорту. Возможность водителя получить доступ к вещам вне его автомобиля, но внутри самолета, даже если этот самолет покидает аэропорт, является закрытием. Вот и все. Когда вам исполнится 27 лет, посмотрите на более подробное объяснение или на пример ниже.
Вот как я могу преобразовать свою историю самолета в код.
источник
Это попытка прояснить несколько (возможных) недоразумений о замыканиях, которые появляются в некоторых других ответах.
источник
Некоторое время назад я написал сообщение в блоге, объясняющее закрытие. Вот то, что я сказал о замыканиях с точки зрения того, почему вы хотите его.
В этом смысле они позволяют функции немного походить на объект с закрытыми атрибутами.
Полный пост:
Так что же это за штуковины?
источник
devError = emailError("devinrhode2@googmail.com", errorString)
а затем иметь свою собственную версию общей функции emailError?Закрытия просты:
Следующий простой пример охватывает все основные моменты замыканий JavaScript. *
Вот фабрика, которая производит калькуляторы, которые можно добавлять и умножать:
Ключевой момент: при каждом вызове
make_calculator
создается новая локальная переменнаяn
, которая продолжает использоваться калькуляторомadd
иmultiply
функциями еще долго послеmake_calculator
возврата.Если вы знакомы со стековыми фреймами, эти калькуляторы кажутся странными: как они могут получить доступ
n
послеmake_calculator
возврата? Ответ заключается в том, чтобы представить, что JavaScript не использует «стековые фреймы», а вместо этого использует «кучу фреймов», которые могут сохраняться после возврата вызова функции, который их сделал.Внутренние функции, такие как
add
andmultiply
, которые обращаются к переменным, объявленным во внешней функции ** , называются замыканиями .Это почти все, что нужно для замыканий.
* Например, он охватывает все пункты в статье «Замыкания для чайников», приведенной в другом ответе , кроме примера 6, в котором просто показано, что переменные можно использовать до того, как они объявлены, - хороший факт, который нужно знать, но совершенно не связанный с замыканиями. Он также охватывает все точки в принятом ответе , за исключением точек (1), в которых функции копируют свои аргументы в локальные переменные (аргументы именованной функции), и (2) что при копировании чисел создается новое число, но копируется ссылка на объект дает вам еще одну ссылку на тот же объект. Это также полезно знать, но опять же совершенно не связано с замыканиями. Это также очень похоже на пример в этом ответе, но немного короче и менее абстрактно. Это не распространяется на точкуэтот ответ или текущего этот комментарийЭто то, что JavaScript затрудняет подключениеЗначение переменной цикла в вашей внутренней функции: шаг «подключения» может быть выполнен только с помощью вспомогательной функции, которая включает вашу внутреннюю функцию и вызывается на каждой итерации цикла. (Строго говоря, внутренняя функция обращается к копии переменной вспомогательной функции, а не подключает что-либо к ней.) Опять же, очень полезно при создании замыканий, но не является частью того, что такое замыкание или как оно работает. Существует дополнительная путаница из-за того, что замыкания работают по-разному в функциональных языках, таких как ML, где переменные связаны со значениями, а не с пространством хранения, предоставляя постоянный поток людей, которые понимают замыкания способом (а именно способом «подключения»), который является просто неверно для JavaScript, где переменные всегда связаны с пространством хранения, а не со значениями.
** Любая внешняя функция, если несколько вложена, или даже в глобальном контексте, как ясно указывает этот ответ .
источник
first_calculator
это объект (а не функция), вы не должны использовать круглые скобкиsecond_calculator = first_calculator;
, поскольку это присваивание, а не вызов функции. Чтобы ответить на ваш вопрос, тогда будет только один вызов make_calculator, поэтому будет выполнен только один калькулятор, а переменные first_calculator и second_calculator будут ссылаться на один и тот же калькулятор, поэтому ответы будут 3, 403, 4433, 44330.Как бы я объяснил это шестилетнему ребенку:
Вы знаете, как взрослые могут владеть домом, и они называют его домом? Когда у мамы есть ребенок, ребенок на самом деле ничего не имеет, верно? Но его родители владеют домом, поэтому, когда кто-то спрашивает ребенка «Где твой дом?», Он / она может ответить «этот дом!» И указать на дом его родителей. «Закрытие» - это способность ребенка всегда (даже если он находится за границей) быть в состоянии сказать, что у него есть дом, даже если дом действительно принадлежит родителю.
источник
Можете ли вы объяснить закрытие 5-летнего ребенка? *
Я все еще думаю, что объяснение Google работает очень хорошо и сжато:
* AC # вопрос
источник
Я склонен учиться лучше, сравнивая ХОРОШЕЕ / ПЛОХОЕ. Мне нравится видеть рабочий код, сопровождаемый нерабочим кодом, с которым кто-то может столкнуться. Я собрал jsFiddle, который делает сравнение и пытается свести различия к простейшим объяснениям, которые я смог придумать.
Затворы сделаны правильно:
В приведенном выше коде
createClosure(n)
вызывается в каждой итерации цикла. Обратите внимание, что я назвал переменную,n
чтобы подчеркнуть, что это новая переменная, созданная в новой области действия функции и не та переменная,index
которая связана с внешней областью действия.Это создает новую область и
n
привязывается к ней; это означает, что у нас есть 10 отдельных областей, по одной на каждую итерацию.createClosure(n)
возвращает функцию, которая возвращает n в этой области.Внутри каждой области видимости
n
привязано к любому значению, которое было у нее приcreateClosure(n)
вызове, поэтому возвращаемая вложенная функция всегда будет возвращать значение,n
которое она имела приcreateClosure(n)
вызове.Замыкания сделаны неправильно:
В приведенном выше коде цикл был перемещен внутри
createClosureArray()
функции, и теперь функция просто возвращает законченный массив, который на первый взгляд кажется более интуитивным.Что может быть неочевидным, так это то, что поскольку
createClosureArray()
он вызывается только один раз, для этой функции создается только одна область видимости, а не одна для каждой итерации цикла.Внутри этой функции определена переменная с именем
index
. Цикл запускается и добавляет функции в массив, которые возвращаютсяindex
. Обратите внимание, чтоindex
это определено внутриcreateClosureArray
функции, которая вызывается только один раз.Поскольку в функции была только одна область действия
createClosureArray()
,index
она связана только со значением в этой области. Другими словами, каждый раз, когда цикл изменяет значениеindex
, он меняет его для всего, что ссылается на него в этой области.Все функции, добавленные в массив, возвращают ту же самую
index
переменную из родительской области, где она была определена, вместо 10 разных из 10 разных областей, как в первом примере. Конечным результатом является то, что все 10 функций возвращают одну и ту же переменную из одной и той же области видимости.После того, как цикл завершился и
index
был изменен, конечное значение равнялось 10, поэтому каждая функция, добавленная в массив, возвращает значение единственнойindex
переменной, которая теперь установлена в 10.Результат
источник
let
дляvar
исправления разницы.n
созданные в новом замыкании. Мы просто возвращаем функцию, чтобы сохранить ее в массиве и вызвать позже.arr[index] = (function (n) { return 'n = ' + n; })(index);
. Но тогда вы сохраняете результирующую строку в массиве, а не функцию для вызова, которая побеждает точку моего примера.Википедия о замыканиях :
Технически, в JavaScript , каждая функция является замыканием . Он всегда имеет доступ к переменным, определенным в окружающей области видимости.
Поскольку определяющая область видимости в JavaScript является функцией , а не блоком кода, как во многих других языках, то , что мы обычно подразумеваем под замыканием в JavaScript , это функция, работающая с нелокальными переменными, определенными в уже выполненной окружающей функции .
Замыкания часто используются для создания функций с некоторыми скрытыми частными данными (но это не всегда так).
Эмс
В приведенном выше примере используется анонимная функция, которая была выполнена один раз. Но это не должно быть. Это может быть названо (например
mkdb
) и выполнено позже, генерируя функцию базы данных каждый раз, когда это вызывается. Каждая сгенерированная функция будет иметь свой собственный скрытый объект базы данных. Другой пример использования замыканий - это когда мы не возвращаем функцию, а объект, содержащий несколько функций для разных целей, каждая из которых имеет доступ к одним и тем же данным.источник
Я собрал интерактивное руководство по JavaScript, чтобы объяснить, как работают замыкания. Что такое закрытие?
Вот один из примеров:
источник
Секреты функций JavaScript - это частные переменные
Каждый раз, когда вы вызываете его, создается локальная переменная «имя» с именем «Мэри». И каждый раз при выходе из функции переменная теряется, а имя забывается.
Как вы можете догадаться, поскольку переменные создаются заново каждый раз, когда вызывается функция, и никто больше не узнает их, должно быть секретное место, где они хранятся. Это можно было бы назвать тайными или стеку или локальной области , но это не имеет никакого значения. Мы знаем, что они где-то спрятаны в памяти.
Но в JavaScript есть такая особенность, что функции, созданные внутри других функций, могут также знать локальные переменные своих родителей и сохранять их в течение всего срока их службы.
Таким образом, пока мы находимся в родительской функции, она может создавать одну или несколько дочерних функций, которые совместно используют секретные переменные из секретного места.
Но грустная вещь в том, что если дочерний элемент также является частной переменной его родительской функции, он также умрет, когда родительский объект закончится, и секреты умрут вместе с ними.
Чтобы жить, ребенок должен уйти, пока не стало слишком поздно
И теперь, несмотря на то, что Мэри «больше не бежит», память о ней не потеряна, и ее ребенок всегда будет помнить ее имя и другие секреты, которыми они поделились во время совместной жизни.
Итак, если вы назовете ребенка "Алиса", она ответит
Это все, что можно сказать.
источник
Я не понимаю, почему ответы здесь такие сложные.
Вот закрытие:
Да. Вы, вероятно, используете это много раз в день.
источник
a
. На мой взгляд, удивительным является то, что объект области действия JS эффективно предоставляетa
как свойство, а не константа. И вы заметите это важное поведение, только если измените его, как вreturn a++;
Пример для первого пункта по dlaliberte:
источник
Закрытие - это когда внутренняя функция имеет доступ к переменным в своей внешней функции. Это, вероятно, самое простое однострочное объяснение, которое вы можете получить для замыканий.
источник
Я знаю, что уже есть множество решений, но я думаю, что этот небольшой и простой скрипт может быть полезен для демонстрации концепции:
источник
Ты переспал и пригласил Дэна. Вы говорите Дэну принести один контроллер XBox.
Дэн приглашает Пола. Дэн просит Пола принести одного контролера. Сколько контролеров было привезено на вечеринку?
источник
Автор Closures довольно хорошо объяснил замыкания, объясняя причину, почему они нам нужны, а также объясняя LexicalEnvironment, что необходимо для понимания замыканий.
Вот резюме:
Что если переменная доступна, но она не локальная? Как здесь:
В этом случае интерпретатор находит переменную во внешнем
LexicalEnvironment
объекте.Процесс состоит из двух этапов:
Когда функция создается, она получает скрытое свойство с именем [[Scope]], которое ссылается на текущую LexicalEnvironment.
Если переменная читается, но нигде не может быть найдена, генерируется ошибка.
Вложенные функции
Функции могут быть вложены друг в друга, образуя цепочку LexicalEnvironments, которую также можно назвать цепочкой областей действия.
Итак, функция g имеет доступ к g, a и f.
Затворы
Вложенная функция может продолжать жить после завершения внешней функции:
Разметка LexicalEnvironments:
Как мы видим,
this.say
это свойство объекта user, поэтому оно продолжает жить после завершения User.И если вы помните, когда
this.say
он создан, он (как и каждая функция) получает внутреннюю ссылкуthis.say.[[Scope]]
на текущую LexicalEnvironment. Таким образом, LexicalEnvironment текущего выполнения пользователя остается в памяти. Все переменные User также являются его свойствами, поэтому они также тщательно сохраняются, а не отбрасываются, как обычно.Весь смысл в том, чтобы гарантировать, что если внутренняя функция захочет получить доступ к внешней переменной в будущем, она сможет это сделать.
Обобщить:
Это называется закрытием.
источник
Функции JavaScript могут получить доступ к своим:
Если функция обращается к своей среде, то функция является замыканием.
Обратите внимание, что внешние функции не требуются, хотя они предлагают преимущества, которые я здесь не обсуждаю. Получая доступ к данным в своей среде, замыкание поддерживает эти данные живыми. В подклассе внешних / внутренних функций внешняя функция может создавать локальные данные и в конечном итоге завершать работу, и, тем не менее, если какая-либо внутренняя функция (и) выживает после выхода из внешней функции, то внутренняя функция (и) сохраняет локальные данные внешней функции. живой.
Пример замыкания, использующего глобальную среду:
Представьте себе, что события кнопок «Переполнение стека» «Голосование вверх» и «Голосование вниз» реализованы в виде замыканий, voiceUp_click и voiceDown_click, которые имеют доступ к внешним переменным isVotedUp и isVotedDown, которые определены глобально. (Для простоты я имею в виду кнопки «Голосовать» в StackOverflow, а не массив кнопок «Голосовать».)
Когда пользователь нажимает кнопку VoteUp, функция voiceUp_click проверяет, является ли isVotedDown == true, чтобы определить, следует ли голосовать за или просто отменить голосование с понижением. Функция VoteUp_click является закрытием, потому что она обращается к своей среде.
Все четыре из этих функций являются замыканиями, поскольку все они имеют доступ к своей среде.
источник
Как отец 6-летнего ребенка, который в настоящее время обучает детей младшего возраста (и относительного новичка в области кодирования без формального образования, поэтому требуются исправления), я думаю, что этот урок лучше всего использовать в практической игре. Если шестилетний ребенок готов понять, что такое закрытие, тогда он достаточно взрослый, чтобы пойти самому. Я бы предложил вставить код в jsfiddle.net, немного пояснить и оставить их одних, чтобы придумать уникальную песню. Пояснительный текст ниже, вероятно, больше подходит для 10-летнего.
ИНСТРУКЦИИ
ДАННЫЕ: Данные представляют собой набор фактов. Это могут быть цифры, слова, измерения, наблюдения или даже просто описания вещей. Вы не можете прикоснуться к этому, почувствовать запах или попробовать это. Вы можете записать это, сказать это и услышать это. Вы можете использовать его для создания сенсорный запах и вкус, используя компьютер. Это может быть полезно на компьютере с помощью кода.
КОД: Все написанное выше называется кодом . Это написано в JavaScript.
JAVASCRIPT: JavaScript это язык. Как английский или французский или китайский языки. Есть много языков, которые понимают компьютеры и другие электронные процессоры. Для того чтобы JavaScript был понятен компьютеру, ему нужен переводчик. Представьте, что учитель, который говорит только по-русски, приходит преподавать ваш класс в школе. Когда учитель говорит «все садятся», класс не понимает. Но, к счастью, у вас в классе есть русский ученик, который говорит всем, что это означает, что «все садятся» - так вы все делаете. Класс подобен компьютеру, а русский ученик - переводчик. Для JavaScript самый распространенный интерпретатор называется браузером.
БРАУЗЕР: при подключении к Интернету на компьютере, планшете или телефоне для посещения веб-сайта вы используете браузер. Примеры, которые вы можете знать, это Internet Explorer, Chrome, Firefox и Safari. Браузер может понимать JavaScript и сообщать компьютеру, что ему нужно делать. Инструкции JavaScript называются функциями.
ФУНКЦИЯ: функция в JavaScript похожа на фабрику. Это может быть небольшая фабрика с одной машиной внутри. Или это может быть много других маленьких фабрик, на каждом из которых много машин делают разные работы. На реальной фабрике одежды у вас могут быть пачки тканей и бобин ниток, а также футболки и джинсы. Наша фабрика JavaScript обрабатывает только данные, она не может шить, сверлить или расплавлять металл. В нашей фабрике JavaScript данные поступают, а данные выходят.
Все эти данные звучат немного скучно, но это действительно очень круто; у нас может быть функция, которая сообщает роботу, что делать на обед. Допустим, я приглашаю вас и вашего друга в мой дом. Тебе больше всего нравятся куриные ножки, я люблю сосиски, твой друг всегда хочет того, что ты хочешь, а мой друг не ест мяса.
У меня нет времени ходить по магазинам, поэтому функция должна знать, что у нас в холодильнике, чтобы принимать решения. Каждый ингредиент имеет разное время приготовления, и мы хотим, чтобы робот одновременно подавал все в горячем виде. Нам нужно предоставить функции данные о том, что нам нравится, функция может «разговаривать» с холодильником, а функция может управлять роботом.
Функция обычно имеет имя, скобки и фигурные скобки. Нравится:
Обратите внимание, что
/*...*/
и//
остановите код, читаемый браузером.ИМЯ: Вы можете вызывать функцию практически из любого нужного вам слова. Пример «cookMeal» типичен для объединения двух слов и введения второго заглавной буквы в начале, но это не обязательно. В нем не может быть пробела, и это не может быть число само по себе.
РОДИТЕЛИ: «круглые скобки» или
()
почтовый ящик на двери фабрики функций JavaScript или почтовый ящик на улице для отправки пакетов информации на фабрику. Иногда почтовый ящик может быть отмечен, напримерcookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, в этом случае вы знаете, какие данные вы должны предоставить.BRACES: «Подтяжки», которые выглядят так,
{}
- это тонированные стекла нашей фабрики. Внутри фабрики вы можете видеть снаружи, но снаружи вы не можете видеть внутри.Пример длинного кода выше
Наш код начинается со слова function , поэтому мы знаем, что он один! Затем название функции sing - это мое собственное описание того, о чем эта функция. Тогда скобки () . Скобки всегда есть для функции. Иногда они пусты, и иногда у них есть что - то в этом один имеет слово.:
(person)
. После этого есть такая скобка{
. Это отмечает начало функции sing () . У него есть партнер, который отмечает конец Sing (), как это}
Так что эта функция может иметь отношение к пению и может потребовать некоторые данные о человеке. Внутри него есть инструкции, чтобы что-то делать с этими данными.
Теперь, после функции sing () , ближе к концу кода находится строка
VARIABLE: буквы var означают «переменная». Переменная похожа на конверт. Снаружи этот конверт помечен как «человек». Внутри он содержит листок бумаги с информацией, которая нужна нашей функции, некоторые буквы и пробелы, соединенные вместе, как кусок строки (это называется строкой), что делает фразу «старая женщина». Наш конверт может содержать другие виды вещей, такие как числа (называемые целыми числами), инструкции (называемые функциями), списки (называемые массивами ). Поскольку эта переменная записана вне всех фигурных скобок
{}
, и поскольку вы можете видеть сквозь тонированные окна, когда находитесь внутри фигурных скобок, эту переменную можно увидеть из любого места в коде. Мы называем это «глобальной переменной».ГЛОБАЛЬНАЯ ПЕРЕМЕННАЯ: человек - это глобальная переменная, означающая, что если вы измените ее значение со «пожилой женщины» на «молодой человек», человек будет оставаться молодым человеком, пока вы не решите изменить его снова и что любая другая функция в Код может видеть, что это молодой человек. Нажмите F12кнопку или посмотрите настройки параметров, чтобы открыть консоль разработчика в браузере, и введите «person», чтобы увидеть, что это за значение. Напечатайте,
person="a young man"
чтобы изменить это, и затем напечатайте "человека" снова, чтобы видеть, что это изменилось.После этого у нас есть линия
Эта строка вызывает функцию, как если бы она вызывала собаку
Когда браузер загрузит код JavaScript, достигший этой строки, он запустит функцию. Я поставил строку в конце, чтобы убедиться, что в браузере есть вся информация, необходимая для его запуска.
Функции определяют действия - основная функция - пение. Он содержит переменную под названием firstPart, которая применяется к пению о человеке, которое относится к каждому из стихов песни: «Был« + человек + », который проглотил». Если вы введете firstPart в консоли, вы не получите ответ, потому что переменная заблокирована в функции - браузер не может видеть внутри окрашенных окон фигурных скобок.
ЗАКРЫТИЯ: замыкания - это меньшие функции, которые находятся внутри большой функции sing () . Маленькие фабрики внутри большой фабрики. Каждый из них имеет свои собственные скобки, которые означают, что переменные внутри них не видны снаружи. Вот почему имена переменных ( существо и результат ) могут повторяться в замыканиях, но с разными значениями. Если вы введете эти имена переменных в окне консоли, вы не получите их значение, потому что оно скрыто двумя слоями тонированных окон.
Все замыкания знают, что такое переменная функции sing () под названием firstPart , потому что они могут видеть из своих тонированных окон.
После закрытия идут линии
Функция sing () будет вызывать каждую из этих функций в том порядке, в котором они заданы. Тогда работа функции sing () будет выполнена.
источник
Ладно, разговаривая с 6-летним ребенком, я бы использовал следующие ассоциации.
Сравните с ситуацией, когда дверь была заблокирована сквозняком и никого внутри (выполнение общих функций), а затем произошел какой-то локальный пожар и сгорел помещение (сборщик мусора: D), а затем была построена новая комната, и теперь вы можете уйти. другие игрушки там (новый экземпляр функции), но никогда не получите те игрушки, которые были оставлены в первом экземпляре комнаты.
Для продвинутого ребенка я бы поставил что-то вроде следующего. Это не идеально, но это заставляет вас чувствовать, что это такое:
Как видите, игрушки, оставленные в комнате, по-прежнему доступны через брата, и не важно, заперта ли комната. Вот jsbin, чтобы поиграть с ним.
источник
Ответ для шестилетнего ребенка (при условии, что он знает, что такое функция, что такое переменная и какие данные):
Функции могут возвращать данные. Один вид данных, который вы можете вернуть из функции - это другая функция. Когда эта новая функция возвращается, все переменные и аргументы, используемые в функции, которая ее создала, не исчезают. Вместо этого родительская функция «закрывается». Другими словами, ничто не может заглянуть внутрь него и увидеть переменные, которые оно использовало, кроме функции, которую оно возвратило. Эта новая функция имеет особую возможность заглянуть внутрь функции, которая ее создала, и просмотреть данные внутри нее.
Другой действительно простой способ объяснить это с точки зрения объема:
Каждый раз, когда вы создаете меньшую область внутри большей области, меньшая область всегда сможет увидеть, что находится в большей области.
источник
Функция в JavaScript - это не просто ссылка на набор инструкций (как в языке C), но она также включает в себя скрытую структуру данных, которая состоит из ссылок на все нелокальные переменные, которые она использует (захваченные переменные). Такие двухсекционные функции называются замыканиями. Каждая функция в JavaScript может считаться закрытием.
Замыкания являются функциями с состоянием. Это несколько похоже на «это» в том смысле, что «это» также предоставляет состояние для функции, но функция и «это» являются отдельными объектами («это» - просто причудливый параметр, и единственный способ навсегда связать его с функция заключается в создании замыкания). Хотя «this» и функция всегда живут отдельно, функцию нельзя отделить от ее закрытия, и язык не предоставляет средств для доступа к захваченным переменным.
Поскольку все эти внешние переменные, на которые ссылается лексически вложенная функция, фактически являются локальными переменными в цепочке своих лексически включающих функций (глобальные переменные можно считать локальными переменными некоторой корневой функции), и каждое отдельное выполнение функции создает новые экземпляры из ее локальных переменных следует, что при каждом выполнении функции, возвращающей (или иным образом передающей ее, например, регистрирующей в качестве обратного вызова) вложенную функцию, создается новое замыкание (со своим потенциально уникальным набором ссылочных нелокальных переменных, которые представляют ее выполнение контекст).
Также следует понимать, что локальные переменные в JavaScript создаются не в кадре стека, а в куче и уничтожаются только тогда, когда на них никто не ссылается. Когда функция возвращается, ссылки на ее локальные переменные уменьшаются, но они все еще могут быть ненулевыми, если во время текущего выполнения они стали частью замыкания и все еще ссылаются на свои лексически вложенные функции (что может произойти, только если ссылки на эти вложенные функции были возвращены или иным образом перенесены в некоторый внешний код).
Пример:
источник
Возможно, немного за пределами всего, кроме самого раннего из шестилетних, но несколько примеров, которые помогли сделать концепцию замыкания в JavaScript, меня зацепили.
Закрытие - это функция, которая имеет доступ к области действия другой функции (ее переменным и функциям). Самый простой способ создать замыкание - использовать функцию внутри функции; причина в том, что в JavaScript функция всегда имеет доступ к области действия своей содержащей функции.
ALERT: обезьяна
В приведенном выше примере вызывается externalFunction, которая, в свою очередь, вызывает innerFunction. Обратите внимание, что externalVar доступен для innerFunction, о чем свидетельствует его правильное оповещение о значении externalVar.
Теперь рассмотрим следующее:
ALERT: обезьяна
referenceToInnerFunction установлен в externalFunction (), которая просто возвращает ссылку на innerFunction. Когда вызывается referenceToInnerFunction, она возвращает externalVar. Опять же, как и выше, это демонстрирует, что innerFunction имеет доступ к outerVar, переменной externalFunction. Кроме того, интересно отметить, что он сохраняет этот доступ даже после того, как externalFunction завершит выполнение.
И вот тут все становится действительно интересно. Если бы мы избавились от externalFunction, скажем, установили его в null, вы могли бы подумать, что referenceToInnerFunction потеряет свой доступ к значению externalVar. Но это не так.
ALERT: обезьяна ALERT: обезьяна
Но как это так? Как referenceToInnerFunction может все еще знать значение externalVar теперь, когда externalFunction был установлен в нуль?
Причина, по которой referenceToInnerFunction по-прежнему может обращаться к значению externalVar, заключается в том, что, когда замыкание было впервые создано путем размещения innerFunction внутри externalFunction, innerFunction добавила ссылку на область действия externalFunction (ее переменные и функции) в свою цепочку областей действия. Это означает, что innerFunction имеет указатель или ссылку на все переменные externalFunction, включая externalVar. Таким образом, даже когда externalFunction закончила выполнение или даже если она удалена или установлена в значение null, переменные в ее области видимости, такие как outerVar, остаются в памяти из-за выдающейся ссылки на них со стороны внутренней функции, которая была возвращена referenceToInnerFunction. Чтобы по-настоящему освободить externalVar и остальные переменные externalFunction из памяти, вам нужно избавиться от этой выдающейся ссылки на них,
//////////
Еще две вещи о замыканиях, чтобы отметить. Во-первых, замыкание всегда будет иметь доступ к последним значениям своей содержащей функции.
ALERT: горилла
Во-вторых, когда создается замыкание, оно сохраняет ссылку на все переменные и функции своей включающей функции; это не может выбирать. И, тем не менее, затворы следует использовать с осторожностью или, по крайней мере, осторожно, так как они могут занимать много памяти; многие переменные могут храниться в памяти еще долго после завершения выполнения содержащей их функции.
источник
Я бы просто указал им на страницу "Мозилла" . Это лучшее, самое краткое и простое объяснение основ закрытия и практического использования, которое я нашел. Настоятельно рекомендуется всем, кто изучает JavaScript.
И да, я бы даже порекомендовал это 6-летнему ребенку - если 6-летний учится о замыканиях, то логично, что они готовы понять краткое и простое объяснение, приведенное в статье.
источник