Как переменные распределяются в памяти в Javascript? - PullRequest
43 голосов
/ 10 мая 2010

Я хотел бы знать, как локальные переменные выделяются памяти в javascript.В C и C ++ локальные переменные хранятся в стеке.Это то же самое в JavaScript?или все хранится в куче?

Ответы [ 2 ]

52 голосов
/ 10 мая 2010

На самом деле это очень интересная область 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                      |
+----------------------------+

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

Подробнее о замыканиях, цепочке областей действия и т. Д. Здесь:

20 голосов
/ 16 мая 2012

К сожалению, ответ: это зависит.

Был большой сдвиг в последних движках javascript, которые начали оптимизировать намного лучше, чем раньше. Раньше ответом было: «Локальные переменные хранятся в выделенных кадрах стека, чтобы замыкания работали». Теперь это не так просто.

Было (или было 20-30 лет назад) исследование для реализации Scheme и оптимизации замыканий (JavaScript унаследовал в значительной степени замыкания Scheme, за исключением продолжений, которые делают его еще сложнее).

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

  • с помощью встроенных функций вы можете значительно сократить количество выделенных / освобожденных кадров, выделенных в куче
  • некоторые переменные могут быть безопасно помещены в стек, так как их временной интервал ограничен (это часто связано также с встраиванием вызовов функций)
  • в некоторых случаях вы знаете, что вы можете создавать замыкание, но вы можете подождать, пока это произойдет, а затем выделить для него кадр стека кучи и скопировать текущие значения из стека
  • есть оптимизации, связанные с хвостовыми вызовами, где вы можете выделить кучу раньше и затем повторно использовать кадр стека для следующего вызова функции, но это не используется в механизмах JavaScript, насколько я знаю в настоящее время

это поле меняется очень быстро в нескольких конкурирующих движках, поэтому ответ, вероятно, все еще будет "это зависит"

Кроме того, в новых версиях языка мы увидим такие функции, как let и const, которые на самом деле облегчают движкам оптимизацию решений о распределении. Особенно неизменность очень помогает, так как вы можете свободно копировать значения из стека (и, например, делать часть объекта замыкания) без разрешения конфликтов изменения переменных из разных замыканий.

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