Время от времени я вижу упомянутое «замыкание», и я пытался найти его, но Вики не дает объяснения, которое я понимаю. Может ли кто-нибудь помочь мне здесь?
language-features
closures
gablin
источник
источник
Ответы:
(Отказ от ответственности: это базовое объяснение; что касается определения, я немного упрощаю)
Самый простой способ думать о замыкании - это функция, которая может храниться как переменная (называемая «функцией первого класса»), которая имеет специальную возможность доступа к другим переменным, локальным для области, в которой она была создана.
Пример (JavaScript):
Функции 1 назначены
document.onclick
иdisplayValOfBlack
являются замыканиями. Вы можете видеть, что они оба ссылаются на логическую переменнуюblack
, но эта переменная назначается вне функции. Посколькуblack
это местное к области , где была определена функция , указатель на этот переменный сохраняется.Если вы поместите это в HTML-страницу:
Это демонстрирует, что оба имеют доступ к одному
black
и тому же и могут использоваться для хранения состояния без какого-либо объекта-оболочки.Вызов должен
setKeyPress
показать, как функция может быть передана так же, как любая переменная. Область действия, сохраненная в замыкании, остается той же, в которой была определена функция.Замыкания обычно используются в качестве обработчиков событий, особенно в JavaScript и ActionScript. Хорошее использование замыканий поможет вам неявно связывать переменные с обработчиками событий, не создавая объектную оболочку. Однако неосторожное использование приведет к утечкам памяти (например, когда неиспользуемый, но сохраненный обработчик событий - единственное, что удерживает большие объекты в памяти, особенно объекты DOM, предотвращая сборку мусора).
1: На самом деле все функции в JavaScript являются замыканиями.
источник
black
как объявлено внутри функции, разве это не будет уничтожено, когда стек раскручивается ...?black
как объявлено внутри функции, разве это не будет уничтожено». Помните также, что если вы объявляете объект в функции, а затем присваиваете его переменной, которая находится где-то еще, этот объект сохраняется, потому что на него есть другие ссылки.Закрытие - это просто другой взгляд на объект. Объект - это данные, с которыми связана одна или несколько функций. Закрытие - это функция, с которой связана одна или несколько переменных. Эти два в основном идентичны, по крайней мере, на уровне реализации. Реальная разница в том, откуда они берутся.
В объектно-ориентированном программировании вы объявляете объектный класс, предварительно определяя его переменные-члены и его методы (функции-члены), а затем создаете экземпляры этого класса. Каждый экземпляр поставляется с копией данных члена, инициализированных конструктором. Затем у вас есть переменная типа объекта, и вы передаете ее как часть данных, потому что основное внимание уделяется ее природе как данных.
С другой стороны, в замыкании объект не определяется заранее, как класс объекта, и не создается при помощи вызова конструктора в вашем коде. Вместо этого вы пишете замыкание как функцию внутри другой функции. Замыкание может ссылаться на любую из локальных переменных внешней функции, и компилятор обнаруживает это и перемещает эти переменные из пространства стека внешней функции в объявление скрытого объекта замыкания. Затем у вас есть переменная типа замыкания, и, хотя в основном это скрытый объект, вы передаете ее как ссылку на функцию, потому что основное внимание уделяется ее природе как функции.
источник
Термин замыкание происходит от того факта, что часть кода (блок, функция) может иметь свободные переменные, которые закрыты (то есть связаны со значением) средой, в которой определен блок кода.
Возьмем для примера определение функции Scala:
В теле функции есть два имени (переменные)
v
иk
обозначение двух целочисленных значений. Имяv
связано, потому что оно объявлено в качестве аргумента функцииaddConstant
(глядя на объявление функции, мы знаем, что ейv
будет присвоено значение при вызове функции). Имяk
является свободным по отношению к функции,addConstant
потому что функция не имеет ни малейшего представления о том, с каким значениемk
связано (и как).Для того, чтобы оценить звонок как:
мы должны присвоить
k
значение, которое может произойти, только если имяk
определено в контексте, в которомaddConstant
оно определено. Например:Теперь, когда мы определились
addConstant
в контексте, гдеk
он определен,addConstant
он стал замыканием, потому что все его свободные переменные теперь закрыты (привязаны к значению):addConstant
их можно вызывать и передавать, как если бы это была функция. Обратите внимание , что свободная переменнаяk
привязана к значению , когда крышка определена , тогда как переменная аргументv
связан , когда крышка вызывается .Таким образом, замыкание - это, в основном, функция или блок кода, который может обращаться к нелокальным значениям через свои свободные переменные после того, как они были связаны контекстом.
На многих языках, если вы используете замыкание только один раз, вы можете сделать его анонимным , например,
Обратите внимание, что функция без свободных переменных является частным случаем замыкания (с пустым набором свободных переменных). Аналогично, анонимная функция является частным случаем анонимного закрытия , то есть анонимная функция является анонимным закрытием без свободных переменных.
источник
Простое объяснение в JavaScript:
alert(closure)
будет использовать ранее созданное значениеclosure
. ПространствоalertValue
имен возвращаемой функции будет связано с пространством имен, в котором находитсяclosure
переменная. При удалении всей функции значениеclosure
переменной будет удалено, но до тех порalertValue
функция всегда сможет прочитать / записать значение переменнойclosure
.Если вы запустите этот код, первая итерация присвоит
closure
переменной значение 0 и перепишет функцию:А поскольку для выполнения функции
alertValue
требуется локальная переменнаяclosure
, она связывается со значением ранее назначенной локальной переменнойclosure
.И теперь каждый раз, когда вы вызываете
closure_example
функцию, она будет записывать увеличенное значениеclosure
переменной, потому чтоalert(closure)
она привязана.источник
«Закрытие» - это, по сути, некоторое локальное состояние и некоторый код, объединенные в пакет. Как правило, локальное состояние происходит из окружающей (лексической) области видимости, а код (по сути) является внутренней функцией, которая затем возвращается наружу. Закрытие - это комбинация захваченных переменных, которые видит внутренняя функция, и кода внутренней функции.
Это одна из тех вещей, которую, к сожалению, трудно объяснить из-за того, что она незнакома.
Одна из аналогий, которую я успешно использовал в прошлом, состояла в том, что «представьте, что у нас есть то, что мы называем« книгой », в закрытии комнаты,« книга »- это копия там, в углу, TAOCP, но на закрытии стола. Это копия книги «Дрезденские файлы». Поэтому, в зависимости от того, в каком закрытии вы находитесь, код «дайте мне книгу» приводит к различным событиям ».
источник
static
локальной переменной считаться замыканием? Завершения в Хаскеле связаны с государством?static
локальной переменной, у вас точно одна).Трудно определить, что такое замыкание, не определяя понятия «государство».
По сути, в языке с полной лексической областью видимости, который рассматривает функции как значения первого класса, происходит нечто особенное. Если бы я сделал что-то вроде:
Переменная
x
не только ссылается,function foo()
но и ссылается на состояние,foo
оставленное в последний раз, когда она возвращалась. Настоящая магия случается, когдаfoo
другие функции дополнительно определены в ее рамках; это похоже на собственную мини-среду (как обычно, мы определяем функции в глобальной среде).Функционально он может решить многие из тех же проблем, что и ключевое слово «static» в C ++ (C?), Которое сохраняет состояние локальной переменной в течение нескольких вызовов функций; однако это больше похоже на применение этого же принципа (статической переменной) к функции, поскольку функции являются значениями первого класса; Закрытие добавляет поддержку для сохранения состояния всей функции (ничего общего со статическими функциями C ++).
Обработка функций как значений первого класса и добавление поддержки замыканий также означает, что в памяти может быть несколько экземпляров одной и той же функции (аналогично классам). Это означает, что вы можете повторно использовать один и тот же код без необходимости сбрасывать состояние функции, как это требуется при работе со статическими переменными C ++ внутри функции (может быть, это неправильно?).
Вот некоторое тестирование поддержки закрытия Lua.
Результаты:
Это может быть сложно, и это, вероятно, варьируется от языка к языку, но в Lua кажется, что всякий раз, когда функция выполняется, ее состояние сбрасывается. Я говорю это потому, что результаты из приведенного выше кода были бы другими, если бы мы обращались к
myclosure
функции / состоянию напрямую (а не через анонимную функцию, которую она возвращает), какpvalue
было бы возвращено к 10; но если мы получим доступ к состоянию myclosure через x (анонимная функция), вы увидите, чтоpvalue
оно живо и хорошо где-то в памяти. Я подозреваю, что есть кое-что еще, возможно, кто-то может лучше объяснить природу реализации.PS: я не знаю, что такое C ++ 11 (кроме того, что было в предыдущих версиях), поэтому учтите, что это не сравнение между замыканиями в C ++ 11 и Lua. Кроме того, все «линии, проведенные» из Lua в C ++, являются сходствами, поскольку статические переменные и замыкания не на 100% одинаковы; даже если они иногда используются для решения подобных проблем.
В чем я не уверен, так это в приведенном выше примере кода, является ли анонимная функция или функция более высокого порядка закрытой?
источник
Закрытие - это функция, связанная с состоянием:
В Perl вы создаете замыкания следующим образом:
Если мы посмотрим на новую функциональность, предоставляемую C ++.
Также позволяет привязать текущее состояние к объекту:
источник
Давайте рассмотрим простую функцию:
Эта функция называется функцией верхнего уровня, потому что она не вложена ни в какую другую функцию. Каждая функция JavaScript связывает с собой список объектов, называемый «Цепью области видимости» . Эта цепочка областей действия представляет собой упорядоченный список объектов. Каждый из этих объектов определяет некоторые переменные.
В функциях верхнего уровня цепочка областей действия состоит из одного объекта - глобального объекта. Например,
f1
вышеприведенная функция имеет цепочку областей действия, в которой есть один объект, который определяет все глобальные переменные. (обратите внимание, что термин «объект» здесь не означает объект JavaScript, это просто объект, определенный реализацией, который действует как контейнер переменных, в котором JavaScript может «искать» переменные.)Когда эта функция вызывается, JavaScript создает нечто, называемое «объект активации» , и помещает его в верхнюю часть цепочки областей действия. Этот объект содержит все локальные переменные (например,
x
здесь). Следовательно, теперь у нас есть два объекта в цепочке областей видимости: первый - это объект активации, а под ним - глобальный объект.Обратите внимание, что два объекта помещаются в цепочку областей видимости в РАЗНОЕ время. Глобальный объект помещается, когда функция определена (т. Е. Когда JavaScript проанализировал функцию и создал объект функции), а объект активации входит, когда функция вызывается.
Итак, теперь мы знаем это:
Ситуация становится интересной, когда мы имеем дело с вложенными функциями. Итак, давайте создадим один:
Когда
f1
определяется, мы получаем цепочку областей действия, содержащую только глобальный объект.Теперь, когда
f1
вызывается, цепочка областей действияf1
получает объект активации. Этот объект активации содержит переменнуюx
и переменную,f2
которая является функцией. И обратите внимание, чтоf2
это уже определено. Следовательно, на этом этапе JavaScript также сохраняет новую цепочку областей видимостиf2
. Цепочка области действия, сохраненная для этой внутренней функции, является текущей действующей цепью области действия. Действующая цепочка областей действия - этоf1
s. Следовательноf2
, цепочка областей видимости -f1
это текущая цепочка областей действия, которая содержит объект активацииf1
и глобальный объект.Когда
f2
вызывается, он получает свой собственный объект активации, содержащийy
, добавленный в его цепочку областей действия, которая уже содержит объект активацииf1
и глобальный объект.Если бы внутри была определена другая вложенная функция
f2
, ее цепочка областей действия содержала бы три объекта во время определения (2 объекта активации двух внешних функций и глобальный объект) и 4 во время вызова.Итак, теперь мы понимаем, как работает цепочка областей действия, но мы еще не говорили о замыканиях.
Большинство функций вызывается с использованием той же цепочки областей действия, которая действовала, когда функция была определена, и на самом деле не имеет значения, что происходит замыкание. Замыкания становятся интересными, когда они вызываются в цепочке областей действия, отличной от той, которая действовала, когда они были определены. Чаще всего это происходит, когда вложенный функциональный объект возвращается из функции, в которой он был определен.
Когда функция возвращается, этот объект активации удаляется из цепочки областей действия. Если не было вложенных функций, больше нет ссылок на объект активации, и он получает мусор. Если были определены вложенные функции, то каждая из этих функций имеет ссылку на цепочку областей действия, и эта цепочка областей ссылается на объект активации.
Однако если эти объекты вложенных функций остаются в пределах их внешней функции, то они сами будут собирать мусор вместе с объектом активации, на который они ссылались. Но если функция определяет вложенную функцию и возвращает ее или сохраняет где-то в свойстве, тогда будет внешняя ссылка на вложенную функцию. Он не будет собирать мусор, и объект активации, на который он ссылается, также не будет собираться мусором.
В нашем приведенном выше примере мы не возвращаемся
f2
изf1
, следовательно, когдаf1
возвращается вызов , его объект активации будет удален из его цепочки областей видимости и собран мусор. Но если бы у нас было что-то вроде этого:Здесь возвращаемый
f2
объект будет иметь цепочку области видимости, которая будет содержать объект активацииf1
, и, следовательно, он не будет собирать мусор. На данный момент, если мы вызовемf2
, он сможет получить доступf1
к переменной,x
даже если мы внеf1
.Следовательно, мы можем видеть, что функция поддерживает цепочку областей видимости, а цепочка областей действия включает все объекты активации внешних функций. Это суть закрытия. Мы говорим, что функции в JavaScript «лексически ограничены» , что означает, что они сохраняют область, которая была активной, когда они были определены, в отличие от области, которая была активной, когда их вызывали.
Существует ряд мощных методов программирования, которые включают замыкания, такие как аппроксимация частных переменных, программирование на основе событий, частичное применение и т. Д.
Также обратите внимание, что все это относится ко всем тем языкам, которые поддерживают замыкания. Например, PHP (5.3+), Python, Ruby и т. Д.
источник
Закрытие - это оптимизация компилятора (он же синтаксический сахар?). Некоторые люди называют это Объектом Бедного Человека .
Смотрите ответ Эрика Липперта : (отрывок ниже)
Компилятор сгенерирует код следующим образом:
Есть смысл?
Кроме того, вы попросили сравнения. VB и JScript создают замыкания практически одинаково.
источник