2014-10-01 6 views
1

Скажем, у нас есть функция:Как выглядит лексическая область в модели памяти?

function foo() { 
    var x = 10;   
    function bar() { 
     var y = 20; 
     return x + y; 
    } 
    return bar(); 
} 

console.log(foo()); 

Что бы это выглядеть в модели памяти. До сих пор это то, что я думаю, это похоже на стек?

TOP OF THE STACK 
-------------- 
bar() 
y = 20 
return x + 20 
-------------- 
foo() 
x= 10 
bar() 
-------------- 
BOTTOM OF THE STACK 

Как выглядит лексическая область, как бар знает, что такое x? Есть foo() на куче? или у bar() есть указатель на foo()?

ответ

1

Ну, после вызов foo завершается, все созданное во время обращения к нему имеет право на сбор мусора (GC), потому что ничего в этом коде не держит ни на что созданный во время разговора. Более интересным вопросом будет то, что произойдет, если fooвозвращаетbar (функция, а не bar() номер, полученный в результате звонка bar).

Но с кодом у вас есть, вот теория о том, что происходит, когда вы звоните foo (определенно в §10.4.3 из спецификации):

  1. Двигатель создает новую декларативную среду, которая изначально лексическая среда и переменная среда для этого конкретного вызова foo (и обычно это не разделяет, ключевое слово with может их отделять, но большинство людей его не используют). Эта декларативная среда имеет связанный с ней объект привязки .

  2. заявленных аргументов foo, имя foo, любые переменные внутри foo объявленные var, имена всех функций, объявленных с помощью объявлений функций, а также несколько других вещей, которые (в определенном порядке), созданные как свойства этот обязательный объект (подробности в §10.5).

  3. Процесс создания bar функции (как описано в §13.2) придает лексическую среду вызова foo функции bar как его [[Scope]] собственности (не буквальное имя вы можете использовать в коде, но имя, используемое в спецификация).

  4. Свойство объекта привязки (например, переменная x) получает значение 10.

  5. Звонок bar создает совершенно новую декларативную среду и т. Д. С переменной y. Объект привязки новой среды имеет ссылку на объект привязки для среды, в которой он был создан. Эта среда получает bar[[Scope]] собственность как ее внешняя лексическая среда ссылка.

  6. Свойство y на внутреннем объекте связывания получает значение 20.

  7. Выражение x + y оценивается:

    1. двигатель пытается разрешить x, чтобы получить его значение. Сначала он обращается к внутреннему объекту привязки, чтобы узнать, имеет ли он свойство с именем x, но это не так.

    2. Двигатель переходит к внешней лексической среде текущего, чтобы увидеть, если он имеют x свойство на его связывание объекта. Так как это делает, движок считывает значение свойства и использует это выражение.

    3. Двигатель пытается решить y, чтобы получить его значение. Сначала он смотрит на самый внутренний объект привязки, чтобы увидеть, имеет ли он свойство, называемое y; он делает, и поэтому движок использует это значение для выражения.

  8. Двигатель завершает выражение, добавив к 2010, помещает результат в стек, и возвращает из bar.

  9. На этом этапе объект окружения и привязки для вызова bar может быть восстановлен через GC.

  10. Двигатель принимает возвращаемое значение от bar, толкает его в стек и возвращает от foo.

  11. На данный момент объект окружения и привязки для вызова foo может быть восстановлен через GC.

  12. Код вызывает 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 закрытий работы.