На самом деле это очень интересная область Javascript.Подробности в спецификации , но: способ обработки локальных переменных в Javascript сильно отличается от способа, которым это делает C.Когда вы вызываете функцию, среди прочего создается «переменная среда» для этого вызова, которая имеет нечто, называемое «объект привязки».(Для краткости назовите его «переменным объектом»; говоря, что «объект привязки переменной среды» - это просто tad bit с большим количеством слов!) У объекта переменной есть свойств дляаргументы функции, все локальные переменные, объявленные в функции, и все функции, объявленные внутри функции (вместе с несколькими другими вещами).Неквалифицированные ссылки (например, foo
в foo
, а не obj.foo
) внутри функции сначала проверяются на предмет объекта переменной, чтобы увидеть, соответствуют ли они свойствам в нем;если они это делают, используются эти свойства.
Когда замыкание сохраняется после возврата функции (что может произойти по нескольким причинам), объект переменной для этого вызова функции остается в памятиСсылка от закрытия.На первый взгляд, это предполагает, что стек не используется для локальных переменных;на самом деле современные движки JavaScript достаточно умны и могут (если это целесообразно) использовать стек для локальных объектов, которые фактически не используются замыканием.(Естественно, стек все еще используется для отслеживания адресов возврата и тому подобного.)
Вот пример:
function foo(a, b) {
var c;
c = a + b;
function bar(d) {
alert("d * c = " + (d * c));
}
return bar;
}
var b = foo(1, 2);
b(3); // alerts "d * c = 9"
Когда мы вызываем foo
, объект переменной создается сэти свойства:
a
и b
- аргументы функции c
- локальная переменная, объявленная в функции bar
- функция, объявленная внутри функции - (... и несколько других вещей)
Когда foo
выполняет инструкцию c = a + b;
, она ссылается на c
, a
и b
свойств объекта переменной для этого вызова foo
.Когда foo
возвращает ссылку на объявленную внутри нее функцию bar
, bar
переживает вызов возврата foo
.Поскольку bar
имеет (скрытую) ссылку на объект переменной для этого конкретного вызова foo
, переменный объект сохраняется (тогда как в обычном случае он не имел бы выдающихся ссылок и поэтому был бы доступен для сборки мусора).
Позже, когда мы вызываем bar
, переменный объект new для этого вызова создается с (среди прочего) свойством d
- аргументом для bar
.Неквалифицированные ссылки в bar
сначала проверяются по объекту переменной для этого вызова;так, например, d
разрешается в свойство d
объекта переменной для вызова bar
.Но неквалифицированная ссылка, которая не соответствует свойству объекта переменной, затем проверяется по следующему объекту переменной в «цепочке областей действия» для bar
, который является объектом переменной для вызова foo
.А поскольку у него есть свойство c
, это свойство используется в bar
.Например, в общих чертах:
+----------------------------+
| `foo` call variable object |
| -------------------------- |
| a = 1 |
| b = 2 |
| c = 3 |
| bar = (function) |
+----------------------------+
^
| chain
|
+----------------------------+
| `bar` call variable object |
| -------------------------- |
| d = 3 |
+----------------------------+
Реализации могут свободно использовать любой механизм, который они хотят использовать под прикрытием, чтобы вышеописанное казалось возможным.Невозможно получить прямой доступ к объекту переменной для вызова функции, и спецификация ясно показывает, что совершенно нормально, если объект переменной является просто концепцией, а не буквальной частью реализации.Простая реализация может буквально делать то, что говорит спецификация;более сложный может использовать стек, когда нет задействованных замыканий (для повышения скорости), или может всегда использовать стек, но затем «отрывать» переменный объект, необходимый для замыкания, при извлечении стека.Единственный способ узнать в каждом конкретном случае, это посмотреть на их код.: -)
Подробнее о замыканиях, цепочке областей действия и т. Д. Здесь: