Просмотр полной версии : Замыкание - это...
Zeroglif
04.03.2009, 22:44
Допустим, вас попросили написать, что такое замыкание в javascript. Только определение, обобщающее суть, без примеров, без воды. Напишите плиз, интересно, кто как это дело видит.
пысы: спрашиваю по мотивам комментариев на этом (http://codeshit.com/post416) блоге
Замыкание - это...
Gvozd:Ключевая возможность замыканий - собственное пространство имен, сохраняемое на протяжении программы.
Андрей Параничев:Замыкание - это вид анонимной функции, которая используется для "замыкания" в себе текущей локальной области видимости, для последующей его передачи в какой-то другой контекст.
Илья Кантор:Если говорить просто, то замыкания - это внутренние функции.
Kolyaj:А я не могу объяснить. :)
AzriMan:Замыкания - это внутренние функции, создаваемые во время (читать: по ходу) выполнения скрипта, имеющие доступ к переменным породившей их функции
Dmitry A. SoshnikovМожно выделить две теории: 1) все функции замыкания (т.к. ... здесь можно привести пару примеров), но (вторая теория) практическую ценность составляет наличие двух пунктов - а) функция переживает свой лексический контекст, б) имеются свободные переменные.
Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. В записи это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.
©Wikipedia.ru (http://ru.wikipedia.org/wiki/Замыкание_(программировани е))
Zeroglif
04.03.2009, 23:45
Gvozd,
То есть ты в целом солидарен с википедией, у которой замыкание - это вложенная функция? Просто мне бы хотелось самодеятельности в определении, т.к. цитат море, а хочется вывести согласованное определение применительно к js.
в целом, я не заморачиваюсь такими вопросами.
я знаю, как их использовать, как они работают, что с их помощью можно сделать в JS
ключевая возможность их по моему мнению- собственное пространство имен, сохраняемое на протяжении программы.
докапыватся до определения-бюракратия.
главное, не чтобы человек мог объяснить определение к примеру "процедуры" и "функции",а умело их использовал на практике.
PS глянул краем глаза дискусию.
ИМХО спор по обсуждению терминологии языка принесет мало пользы.
мое мнение по поводу спорного момента, что да, там двойное замыкание, и хотя ф-ция первого замыкания не переживает никого,и я вляется анонимной она все равно замыкание
Андрей Параничев
05.03.2009, 01:12
Для меня замыкание это вид анонимной функции, которая используется для "замыкания" в себе текущей локальной области видимости, для последующей его передачи в какой-то другой контекст.
ключевая возможность их по моему мнению- собственное пространство имен
Я тоже так думал, но все-таки не совсем это достаточное условие, чтоб функцию можно было назвать замыканием. Например, создавая FunctionExpression мы тоже создаем отдельный скоп (обычно их для этого и делают), но является ли это замыканием - вопрос спорный.
Zeroglif
05.03.2009, 10:20
главное, не чтобы человек мог объяснить определение к примеру "процедуры" и "функции",а умело их использовал на практике
Всё так, ну, вот тебя ньюб (я, например) спросил простым текстом, что такое замыкание в javascript. А ты ему сначала цитату из википедии про "ссылки на переменные", а потом свою цитату про "собственное пространство имен, сохраняемое на протяжении программы", из первой цитаты он решит, что замыкания - это исключительно вложенные функции со "ссылками", из второй, что это все функции без исключения. Так все или вложенные? С переменными или без?
да, там двойное замыкание, и хотя ф-ция первого замыкания не переживает никого,и я вляется анонимной она все равно замыкание
Понятно, то есть все функции в js - замыкания безусловно. Позицию понял.
Для меня замыкание это вид анонимной функции
То есть ты считаешь, что функция должна быть анонимной?
Dmitry A. Soshnikov
05.03.2009, 12:24
Итак, вернёмся с того блога (http://codeshit.com/post416) к этому вопросу.
Если попробовать описать ещё более обще, и, в альтернативу "замыкание - функция", поставить "функцию" в пассивный залог (как объект замыкания), то также можно сказать, что замыкание - это эффект, при котором:
а) область видимости (scope) контекста, породившего блок кода, а также, все вышестоящие области видимости, доступны этому порождённому блоку кода;
б) порождённый блок кода может "пережить" порождающий его контекст.
--- Специально не написал в общем определении - "функция", т.к. в Ruby, например, замыкание - это либо блок (block), либо lambda, либо Proc, но касательно JavaScript, будем говорить "функция". Также, упростив, будем говорить, что замыкание - это функция, а не эффект (к тому же, на основную суть это не влияет). ---
Обязательно ли наличие сразу двух пунктов, чтобы назвать функцию замыканием?
Теоретически и практически, пункт а) реализуется обычной функцией в глобальном скопе:
var a = 10;
function x() {
alert(a);
}
x(); // 10
Что произошло:
- глобальный контекст породил функцию "x";
- функция "x" получила ссылку на scope глобального контекста (это её [[scope]]; подробней можно почитать в стандарте и статьях, либо здесь можно будет рассказать, если потребуется);
- при активации функции создалась цепь скопов (scope chain, Scope, - с большой буквы обозначается): Scope = [[scope]] + текущий Variable Object (VO) функции (в новых редакциях - Environment Object, EO), в которой будут искаться переменные, используемые в функции;
- переменная "а" не определена в VO(x), т.е. является свободной для функции "x" и будет найдена в скопе window;
Таким образом, первое условие, если рассматривать общий механизм (поиск переменных в scope chain), реализуемо на любых функциях (не важно - анонимных или нет, вложенных или глобальных). Это к теории, что все функции - замыкания (когда основным моментом считается наличие свободных переменных). Если же конкретизировать, здесь можно разделять (как это сделано в Википедии) лексические и глобальные переменные. Однако, мне это разделение кажется условным, поскольку, повторю, механизм цепи скопов - одинаков.
Однако, если пойти дальше, и, всё-таки, попытаться разделять функции на "замыкания" и "не-замыкания", то нужно рассмотреть пункт б). Обратимся к нему.
Глобальная функция не может "переживать" свой контекст, т.к. глобальный контекст никуда не "умирает"; он существует на протяжении жизни запущенной программы. В свою очередь вложенная функция такую возможность имеет:
var a = 10;
function y() {
return function z() {
alert(a);
};
}
dz = y();
dz(); // 10
Снова видно, что функция "z" имеет на борту свободную переменную "а", однако, эта переменная не определена в породившем функцию "z" скопе (контекст функции "y"), но определена в скопе выше. Плюс ко всему - функция пережила породивший её контекст.
Кстати, если бы мы не написали alert(a) (фактически нет свободных переменных), то это не значит, что функция "z" не имеет доступ как к скопу "y", так и к вышестоящему (глобальному) скопу. Этот [[scope]], как было сказано выше, попадает в функцию "z" при её создании.
Стало быть, теоретически достаточно этого пунка б), чтобы сделать разделение на "замыкания" и "не-замыкания". Теоретически, - т.к. пункт а) (свободные переменные) выполняется неявно - из глобального контекста.
Но, всё же, с прикладной точки зрения, этот механизм удобен, когда пункт а), наряду с обязательным пунктом б) (в теории разделения на "замыкания" и "не-замыкания") является явным, т.е. свободные переменные объявлены в скопах ниже глобального, но выше родного VO (EO) функции.
Поэтому, подытожив, можно остановится на твоём описании, Zeroglif, как более полном: есть теория, что все функции - замыкания (однако, здесь - только теоретический интерес), но с практической точки зрения - должны выполняться "по-полной" оба пункта второй теории (с разделением на "замыкания" и "не-замыкания").
update:
А, да. И о терминах: "двойное", "тройное", "десятикратное" замыкание. Естественно, термины прикладные (не академические), т.к. более относятся к уровню вложенности scope chain и не являются основной характеристикой замыканий. Хотя, для удобства можно и так, наверное, говорить. Но только не в ключе - "какая интересная и "страшная" вещь - двойное замыкание".
update2:
Только определение, обобщающее суть, без примеров, без воды
А, пардон, не обратил особого внимания на эту просьбу, подробно расписал. Ок. Только суть: можно выделить две теории: 1) все функции замыкания (т.к. ... здесь можно привести пару примеров), но (вторая теория) практическую ценность составляет наличие двух пунктов - а) функция переживает свой лексический контекст, б) имеются свободные переменные.
замыкания - это внутренние функции, создаваемые во время (читать: по ходу) выполнения скрипта, имеющие доступ к переменным породившей их функции
Dmitry A. Soshnikov
05.03.2009, 14:00
замыкания - это внутренние функции
А почему именно внутренние?
Zeroglif
06.03.2009, 11:07
Dmitry A. Soshnikov,
Спасибо за подробные размышления, чем больше думаю, тем больше слоняюсь к мысли, что условия не нужны, в смысле "все функции замыкания и точка". ;)
Dmitry A. Soshnikov
06.03.2009, 22:04
тем больше слоняюсь к мысли, что условия не нужны
Возможно. Возможно, в JS оно и так. Хотя, для полного определения, всё же, наверное, лучше две теории упоминать.
Но, сегодня более глубоко посмотрел, как это дело обстоит в Python'е (да-да, опять Питон ;)). Так вот. Там так же работает механизм цепи скопов (т.е. из вложенных функций мы можем достучаться до всех вышестоящих переменных/объектов, включая глобальный скоп). Однако, начиная с версии Python'a 3.0, у функций есть интересное свойство - __closure__, которое:
None or a tuple of cells that contain bindings for the function’s free variables.
Можно почитать здесь - http://docs.python.org/3.0/reference/datamodel.html, конкретно раздел "Callable types".
Это свойство, как было сказано, хранит кортеж (tuple, неизменяемый массив) свободных переменных функции. Т.е. это тот самый [[scope]] из JS, который записался в функцию при её создании, только здесь мы имеем к нему доступ явно. Но! Самое интересное здесь то, что глобальные переменные туда не входят! Здесь, если функция глобальная - у неё, считается, нет свободных лексических локальных переменных (и в этом случае, __closure__ == None):
a = 10
# глобальная функция
def x():
print(a)
x() # 10, работает, но:
x.__closure__ # None
А теперь с вложенными функциями:
a = 10
def x():
b = 20
def y(): # первая вложенная
c = 30
def z(): # вторая вложенная
print(a, b, c)
return z # возврат из y
return y # возврат из x
# проверка
dy = x() # вернувшаяся функция "y"
dz = dy() # вернувшаяся функция "z", можно было сразу - x()()
dz() # 10, 20, 30
# замыкание внешней функции "x"
x.__closure__ # None
# замыкание внутренней функции "y" (dy)
dy.__closure__ # (<cell at 0x0143AE70: int object at 0x1E1E5678>,) - кортеж из одного элемента
dy.__closure__[0] # <cell at 0x0143AE70: int object at 0x1E1E5678> - этот самый элемент, объект cell
dy.__closure__[0].cell_contents # а вот она наша 20!
dy.__closure__[1] # ошибка! нет элемента с индексом 1, только одна свободная переменная, глобальная "а" сюда не входит
# замыкание внутренней функции "z" (dz)
dz.__closure__ # два объекта, как и ожидалось - (<cell at 0x0143E970: int object at 0x1E1E5718>, <cell at 0x0143AE70: int object at 0x1E1E5678>)
dz.__closure__[0].cell_contents # 30
dz.__closure__[1].cell_contents # 20
dz.__closure__[2].cell_contents # ошибка, только две свободные переменные
Т.е. здесь, обособление лексических переменных от глобальных представлено явно. И именно лексические переменные попали в замыкание.
P.S.:> Zeroglif, скачай, поставь интерпретатор Python'a, тоже, как и JS, интересно.
P.S.[2]:> сейчас ещё и Ruby посмотрел - там тоже есть явное разделение на глобальные переменные (префикс - $: $a, $b и т.д.), и локальные - без префиксов. Так вот глобальные переменные могут использоваться в методах (def'ы), в то время как локальные для глобального контекста - нет:
a = 10
def x
puts a
end
x # ошибка, переменная "а" не определена
$b = 10
def y
puts $b
end
y # 10, все Ок
В свою очередь замыкания в Ruby, которые создаются через block'и, lambda'ы и Proc'ы, запоминают эти лексические переменные:
a = 10
x = lambda {
puts a
}
x.call # 10
В общем, сколько идей, столько и реализаций ;)
vBulletin® v3.6.7, Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot