Мне было любопытно, как js-код выполняется от начала до конца.

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

Вопрос:

Когда V8 начинает компилировать и выполнять код применительно к стеку цикла событий?

Это когда функция вот-вот выскочит из стека?

Или все функции компилируются прямо перед тем, как они помещаются в стек?

Поэтому процесс помещения другой функции поверх на самом деле имеет дело только с машинным кодом, если это так, происходит ли выполнение этого машинного кода при извлечении функции из стека?

В случае, если мой вопрос не понят, я считаю, что с помощью этого примера было бы лучше понять

Пример :

function foo() {
    var name=`foo`;
    var obj = {
        number: 6
    }
    console.log(obj.number);
}

function baz() {
    var name = `baz`;
    console.log(a);
    foo();
}

baz();
  1. первый процесс, который происходит, - это ленивый анализ , где весь файл анализируется на предмет синтаксических ошибок, но не анализируется полностью, поэтому это занимает меньше времени.
  2. прохождение функции declerations

    • теперь движок v8 компилирует код объявления функции в Машинный код ? или его очередь еще не ...
  3. baz вызывается, baz помещается в нижнюю часть стека, и в его фрейме стека сохраняется значение переменной name (поскольку это примитив).

    • когда именно buz анализируется и преобразуется в машинный код? до того, как он будет помещен в стек? или когда выскакивает?
  4. console.log помещается поверх baz и выполняется, - console показывает baz

    • это место, где js-код console.log компилируется в машинный код и выполняется?
  5. console.logs всплывает из стека.

  6. foo помещается поверх baz, obj помещается в кучу (поскольку это ссылочный тип), а name=foo помещается во фрейм стека foo.

  7. console.log находится поверх foo и выполняется, console показывает 6.

  8. console.log выскакивает.
  9. foo появляется вместе со значением локальной переменной.
  10. baz появляется вместе со своей локальной переменной name=baz
0
zzzz 8 Дек 2018 в 23:02

1 ответ

Лучший ответ

Не существует такого понятия, как «стек цикла событий».

Одно из понятий - это «стек вызовов», который является термином для обозначения того факта, что, когда функции вызывают друг друга, они формируют текущее состояние вещей, подобное стеку. Это в основном теоретическая концепция, но, как оказалось, действительно существует область памяти, которая называется «стеком» и используется для локальных переменных функций, но это не структура данных с интерфейсом push / pop: При вызове функции данные помещаются в этот стек, а при возврате из функции они снова удаляются, возвращая управление вызывающей функции.

Это отвечает на часть вашей функции: начало выполнения функции буквально совпадает с помещением этой функции в стек вызовов. Это два описания одного и того же.

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

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

Поскольку вы спрашивали конкретно о V8: в текущих версиях, когда V8 видит определение функции вроде function f() { ... }, он еще ничего не делает (за исключением нескольких случаев, когда V8 предполагает, что функция скоро будет выполнена, в в этом случае он немедленно создает для него байт-код). Если функция ставится в очередь как обратный вызов, синтаксический анализ или компиляция все равно не выполняется. Когда функция вызывается в первый раз, V8 создает для нее байт-код. Когда функция вызывается снова, байт-код уже существует, поэтому никаких дополнительных действий не требуется. Когда функция становится достаточно горячей, V8 в конечном итоге решает скомпилировать для нее оптимизированный машинный код, обычно в фоновом потоке. Дополнительные вызовы к нему - это возможность для V8 проверить, закончил ли фоновый поток уже создание машинного кода; если это так, то следующий вызов будет использовать этот оптимизированный код вместо интерпретации байт-кода функции, как это делали предыдущие вызовы. Обратите внимание, что эти детали реализации могут и будут меняться со временем.

Еще одно примечание для пояснения:

в его стековом фрейме хранится значение переменной name (так как это примитив).

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

2
jmrk 10 Дек 2018 в 20:01