Ну, после вызов foo
завершается, все созданное во время обращения к нему имеет право на сбор мусора (GC), потому что ничего в этом коде не держит ни на что созданный во время разговора. Более интересным вопросом будет то, что произойдет, если foo
возвращаетbar
(функция, а не bar()
номер, полученный в результате звонка bar
).
Но с кодом у вас есть, вот теория о том, что происходит, когда вы звоните foo
(определенно в §10.4.3 из спецификации):
Двигатель создает новую декларативную среду, которая изначально лексическая среда и переменная среда для этого конкретного вызова foo
(и обычно это не разделяет, ключевое слово with
может их отделять, но большинство людей его не используют). Эта декларативная среда имеет связанный с ней объект привязки .
заявленных аргументов foo
, имя foo
, любые переменные внутри foo
объявленные var
, имена всех функций, объявленных с помощью объявлений функций, а также несколько других вещей, которые (в определенном порядке), созданные как свойства этот обязательный объект (подробности в §10.5).
Процесс создания bar
функции (как описано в §13.2) придает лексическую среду вызова foo
функции bar
как его [[Scope]]
собственности (не буквальное имя вы можете использовать в коде, но имя, используемое в спецификация).
Свойство объекта привязки (например, переменная x
) получает значение 10
.
Звонок bar
создает совершенно новую декларативную среду и т. Д. С переменной y
. Объект привязки новой среды имеет ссылку на объект привязки для среды, в которой он был создан. Эта среда получает bar
[[Scope]]
собственность как ее внешняя лексическая среда ссылка.
Свойство y
на внутреннем объекте связывания получает значение 20
.
Выражение x + y
оценивается:
двигатель пытается разрешить x
, чтобы получить его значение. Сначала он обращается к внутреннему объекту привязки, чтобы узнать, имеет ли он свойство с именем x
, но это не так.
Двигатель переходит к внешней лексической среде текущего, чтобы увидеть, если он имеют x
свойство на его связывание объекта. Так как это делает, движок считывает значение свойства и использует это выражение.
Двигатель пытается решить y
, чтобы получить его значение. Сначала он смотрит на самый внутренний объект привязки, чтобы увидеть, имеет ли он свойство, называемое y
; он делает, и поэтому движок использует это значение для выражения.
Двигатель завершает выражение, добавив к 20
10
, помещает результат в стек, и возвращает из bar
.
На этом этапе объект окружения и привязки для вызова bar
может быть восстановлен через GC.
Двигатель принимает возвращаемое значение от bar
, толкает его в стек и возвращает от foo
.
На данный момент объект окружения и привязки для вызова foo
может быть восстановлен через GC.
Код вызывает console.log
с результатом. (подробности опущены.)
Таким образом, в теории, не Продолжительное воздействие памяти. Среда и объекты привязки могут быть переброшены.
В настоящее время в факте современные двигатели JavaScript очень умны и используют стек для определенных распределений объектов, так что им не нужно вызывать GC для восстановления этих сред и объектов привязки. (. Но продолжайте читать)
Теперь предположим, что foo
выглядит следующим образом:
function foo() {
var x = 10;
function bar() {
var y = 20;
return x + y;
}
return bar;
}
И мы сделали это:
var b = foo();
Теперь foo
возвращает ссылку на bar
(не называя его) ,
шаги 1-4 выше, остаются неизменными, но тогда вместо вызоваbar
, foo
возвращает ссылку на него.Это означает, что объект окружения и привязки, созданный при вызове foo
, не соответствует, поскольку функция bar
, созданная во время этого вызова, имеет ссылку на них, и у нас есть ссылка на эту функцию (через переменную b
). Таким образом, в теории в этой точке, что-то вроде этого существует в куче:
+-----+ +-------------+
| b |---->| Function |
+-----+ +-------------+
| name: "bar" | +----------------+
| [[Scope]] |---->| environment |
+-------------+ +----------------+ +-------+
| Binding Object |---->| x: 10 |
+----------------+ +-------+
Так что если современные двигатели умны о выделении этих объектов на стеке (иногда), как они могут по-прежнему существовать после foo
возвращается? Конечно, вам придется копать внутренности отдельных двигателей. Некоторые из них, вероятно, выполняют статический анализ, чтобы увидеть, возможна ли ситуация и использовать распределение кучи с самого начала, если объект привязки может выжить. Некоторые могут просто определить, когда возвращается foo
, что должно выжить и скопировать эти вещи из стека в кучу. Или [вставить действительно умный материал для компилятора здесь]. Некоторые двигатели могут быть достаточно умными, чтобы сохранять только те вещи, на которые можно ссылаться (поэтому, если у вас были переменные в foo
, которые никоим образом не ссылались на bar
, их можно было бы обрезать из объекта привязки). Высокий уровень, спецификация требует, чтобы он казался, как и структура выше, сохраняется в памяти, что мы ничего не можем сделать в нашем коде, может доказать, что это не то, что произошло.
Если мы тогда позвоним b
, мы поднимаем с помощью шагов выше, выполняя шаги с 5 по 10, но когда возвращается b
, структура выше продолжает существовать.
Это как JavaScript закрытий работы.