Требует ли (JS) закрытия функции внутри функции - PullRequest
8 голосов
/ 17 августа 2011

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

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

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

(function(){var a = 1;}())

Является (не очень полезным) примером закрытия. Или, черт возьми, даже просто так:

function(){var a = 1;}

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

function(foo){foo.a = 1;}(bar); // bar.a = 1

или даже (для обеспечения неизменяемости):

function(foo){var a = 1; bar.baz = function() { return a}}(bar); // bar.baz() = 1

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

EDIT

Только что заметил определение вики для тега "замыкания" в переполнении стека:

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

В то время как SO wiki определенно не является окончательным авторитетом, первое предложение, похоже, коррелирует с моим пониманием этого термина. Во втором предложении предлагается, как можно использовать замыкание, но оно не выглядит как .

РЕДАКТИРОВАТЬ # 2

В случае, если это не ясно из различных ответов здесь, ответа в Википедии и ответа тега, кажется, нет четкого консенсуса относительно того, что вообще означает слово «закрытие». Так что, хотя я до сих пор ценю все ответы, и все они имеют смысл, если вы согласитесь с авторским определением замыкания, я думаю, что на самом деле я ищу ... есть ли какое-либо реальное "авторитетное" определение слова ( и если да, то как это применимо ко всему вышеописанному)?

Ответы [ 3 ]

9 голосов
/ 17 августа 2011

Вас сбивает с толку неверное предположение о том, откуда взято слово "замыкание".

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

Почему тогда странное слово?Посмотрите на функцию в вашем последнем примере:

bar.baz = function() { return a }

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

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

Приложение : В общей идиоме

function() {
   var blah;
   // some code here
}();

смысл не , чтобы получить замыкание (вы, конечно, получите одно, но этоне делает ничего интересного для вас), но создает локальную область видимости для переменной blah.Локальная область действия концептуально совсем не похожа на замыкание - на самом деле большинство C-двойников, кроме Javascript, будут создавать их в каждом блоке {}, тогда как они могут иметь или не иметь замыкания вообще.

2 голосов
/ 17 августа 2011

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

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

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

Например, в моем TIScript кадры вызовов заменяются в стеке - при выходе из функции ее кадр вызова, который включает в себя набор переменных, которые он использует, удаляется из стека. Создание замыкания в моем случае происходит, когда: VM встречает инструкцию объявления функции, и эта функция помечается (компилятором) как та, которая использует внешние переменные. В этом случае текущая цепочка фреймов вызовов, которая содержит используемые переменные, перемещается из стека в кучу - преобразуется в объекты данных GCable и ссылается на функцию и , ее цепочка вызовов сохраняется в качестве ссылки.

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

Но это:

function(foo){  
   var a = 1; 
   bar.baz = function() { return a; }; 
}

создает замыкание в поле bar.baz. При последующем вызове bar.baz() выполняется код функции и значение переменной 'a' будет взято из ссылки на внешний кадр вызова, который хранится в замыкании.

Надеюсь, это кое-что прояснит для вас.

0 голосов
/ 17 августа 2011

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

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