tl; dr answer: "В V8 выделяется куча только переменных, на которые ссылается внутренний fns. Если вы используете eval, тогда все переменные предполагаются ссылочными." .Во втором примере o2
может быть выделено в стеке и выброшено после выхода f1
.
Я не думаю, что они справятся с этим.По крайней мере, мы знаем, что некоторые движки не могут этого сделать, так как известно, что это является причиной многих утечек памяти, например:
function outer(node) {
node.onclick = function inner() {
// some code not referencing "node"
};
}
, где inner
закрывается за node
, образуя циклическую ссылку inner -> outer's VariableContext -> node -> inner
, который никогда не будет освобожден, например, в IE6, даже если узел DOM удален из документа.Некоторые браузеры справляются с этим очень хорошо: сами циклические ссылки не являются проблемой, проблема в реализации GC в IE6.Но теперь я отступлю от темы.
Распространенный способ разрыва циклической ссылки - обнулить все ненужные переменные в конце outer
.Т.е. установить node = null
.Тогда возникает вопрос, могут ли современные движки javascript сделать это для вас, могут ли они как-то сделать вывод, что переменная не используется в inner
?
Я думаю, что ответ - нет, но я могу доказать, что это неправильно.Причина в том, что следующий код выполняется просто отлично:
function get_inner_function() {
var x = "very big object";
var y = "another big object";
return function inner(varName) {
alert(eval(varName));
};
}
func = get_inner_function();
func("x");
func("y");
Убедитесь сами, используя этот пример jsfiddle .Внутри inner
нет ссылок на x
или y
, но они по-прежнему доступны с помощью eval
.(Удивительно, но если вы псевдоним eval
что-то еще, скажем myeval
, и вызываете myeval
, вы НЕ получите новый контекст выполнения - это даже в спецификации, см. Разделы 10.4.2 и 15.1.2.1.1 в ECMA-262.)
Редактировать: В соответствии с вашим комментарием, похоже, что некоторые современные движки действительно делают некоторые умные трюки, поэтому я попытался выкопать немного больше.Я наткнулся на эту ветку форума , в которой обсуждается проблема, и в частности ссылку на твит о том, как переменные распределяются в V8 .Это также особенно касается проблемы eval
.Кажется, что он должен анализировать код во всех внутренних функциях.и посмотрите, на какие переменные ссылаются, или если используется eval
, а затем определите, должна ли каждая переменная быть размещена в куче или в стеке.Довольно аккуратно.Вот еще один блог , который содержит много деталей о реализации ECMAScript.
Это означает, что даже если внутренняя функция никогда не «избегает» вызова, она все равно может принудительно заставить переменныераспределяться по куче.Например:
function init(node) {
var someLargeVariable = "...";
function drawSomeWidget(x, y) {
library.draw(x, y, someLargeVariable);
}
drawSomeWidget(1, 1);
drawSomeWidget(101, 1);
return function () {
alert("hi!");
};
}
Теперь, когда init
завершил свой вызов, на someLargeVariable
больше нет ссылок, и он должен иметь право на удаление, но я подозреваю, что это не так, если только внутренняя функция drawSomeWidget
был оптимизирован (встроенный?).Если это так, то это может происходить довольно часто при использовании самозапускающихся функций для имитации классов с закрытыми / открытыми методами.
Ответ на комментарий Райноса ниже.Я попробовал описанный выше сценарий (слегка измененный) в отладчике, и результаты, как я предсказываю, по крайней мере в Chrome:
Когда выполняется внутренняя функция, someLargeVariable все еще находится в области видимости.
Если я закомментирую ссылку на someLargeVariable
во внутреннем методе drawSomeWidget
, тогда вы получите другой результат:
Теперь someLargeVariable
не входит в область действия, посколькуможет быть размещен в стеке.