Понимание захвата переменной закрытия JavaScript в v8 - PullRequest
0 голосов
/ 31 декабря 2018

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

Я также понимаю, что переменные в одной и той же области могут обрабатываться по-разному, в зависимости от того, были ли они захвачены замыканиями в современном движке javascript.например,

function foo(){
    var a=2;
    var b=new Array(a_very_big_number).join('+');
    return function(){
        console.log(a);
    };
}
var b=foo();

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

Мой вопрос таков: почему v8, по-видимому, упаковывает все переменные, на которые ссылаются все замыкания, вместе в каждом вызывающем контексте?например,

function foo(){
    var a=0,b=1,c=2;
    var zig=function(){
        console.log(a);
    };
    var zag=function(){
        console.log(b);
    };
    return [zig,zag];
}

и zig, и zag, кажется, содержат ссылку на a и b, даже если очевидно, что b недоступен для zig.Это может быть ужасно, когда b очень большой, а zig сохраняется очень долго.

Но стоит с точки зрения реализации, я не могу понять, почему это необходимо.Исходя из моих знаний, без вызова eval, цепочка областей действия может быть определена до освобождения, таким образом, может быть определена ссылочная связь.Движок должен знать, что когда zig больше не доступен, то не делайте a, поэтому двигатель помечает его как мусор.

Кажется, что и chrome, и firefox подчиняются правилу.Стандарт говорит, что любая реализация должна сделать это?Или эта реализация более практичная, более эффективная?Я довольно озадачен.

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Основным препятствием является изменчивость.Если два замыкания имеют одно и то же значение var, они должны сделать это так, чтобы мутировавшее из одного замыкания было видно в другом.Следовательно, невозможно скопировать значения указанных переменных в каждую среду замыкания, как это сделали бы функциональные языки (где привязки неизменны).Вам нужно совместно использовать указатель на общее местоположение изменяемой кучи.

Теперь вы можете выделить каждую захваченную переменную как отдельную ячейку в куче вместо одного массива, содержащего все.Однако это часто будет дороже в пространстве и времени, потому что вам потребуется несколько распределений и два уровня косвенности (каждое замыкание указывает на свою собственную среду замыкания, которая указывает на каждую ячейку общей изменяемой переменной).В текущей реализации для доступа к переменной требуется только одно выделение на область и одно косвенное обращение (все замыкания в пределах одной области указывают на один и тот же массив изменяемых переменных).Недостатком является то, что определенные времена жизни длиннее, чем вы могли ожидать.Это компромисс.

Другими соображениями являются сложность реализации и возможность отладки.С сомнительными функциями, такими как eval, и ожиданиями, что отладчики могут проверять цепочку областей действия, реализация на основе областей будет более гибкой.

0 голосов
/ 31 декабря 2018

Стандарт ничего не говорит о сборке мусора, но дает некоторые подсказки о том, что должно произойти.Справка: Стандарт

Внешняя Лексическая Среда может, конечно, иметь свою собственную внешнюю Лексическую Среду.Лексическая среда может служить внешней средой для нескольких внутренних Лексических сред.Например, если объявление функции содержит два вложенных объявления функции, то лексические среды каждой из вложенных функций будут иметь в качестве своей внешней лексической среды лексическую среду текущего выполнения окружающей функции. "

Section 13 Function definition
  step 4: "Let closure be the result of creating a new Function object as specified in 13.2"

Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)

Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.

Итак, функция будет иметь доступ к окружению родителя.

...