Объяснение как работает JavaScript Engine с помощью визуализации

Объяснение как работает JavaScript Engine с помощью визуализации

JavaScript девелоперам обычно не приходится иметь дело с компиляторами, тем не менее, будет полезно знать основы движка джаваскрипта и понимать как он обрабатывает js код. 

Механизм JavaScript может быть реализован как стандартный интерпретатор или just-in-time компилятор, который компилирует JavaScript в байт-код в той или иной форме. Первые движки JavaScript были почти только интерпретаторами, но большинство современных движков используют JIT-компиляцию для повышения производительности.

Flow работы Javascript Engine

давайте рассмотрим упрощенную схему работы движка:

джаваскрипт движок схема

 

Parser

Первоначально парсер HTML обнаруживал тег скрипта с источником JavaScript. Исходный код в этом сценарии будет загружен как поток байтов UTF-16 в декодер потока байтов. Эти байты декодируются в токены, которые пересылаются синтаксическому анализатору. Движок всегда будет избегать синтаксического анализа кода, который в настоящее время не нужен, чтобы быть более эффективным.

js engine

Byte stream decoder создает токены из декодированного потока байтов. 

byte js

JS engine юзает 2 парсера: pre-parser и parser. Чтобы сократить время, необходимое для загрузки веб-сайта, движок пытается избежать парсинг кода, который не нужен сразу. Pre-parser обрабатывает код, который может быть использован позже, в то время как parser обрабатывает код, который нужен немедленно. Если определенная функция будет вызвана только после того, как пользователь нажмет кнопку, нет необходимости, чтобы этот код компилировался немедленно, просто чтобы загрузить веб-сайт. Если пользователь в конечном итоге нажимает кнопку и запрашивает этот фрагмент кода, он отправляется парсеру.

AST

Анализатор создает ноды на основе токенов, которые он получает от byte stream decoder'а. С помощью этих узлов он создает абстрактное синтаксическое дерево (AST)🌳.

ast

Интерпритатор

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

интерпритатор js

Profiler & Compiler

Байт код работает быстро, но всегда можно быстрее, по-этому, в js engine есть еще optimizing compiler. Он принимает байтовый код + "type feedback" и генерирует из них высоко оптимизированный машинный код. 🚀

optimized

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

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

Скажем, определенная функция вызывается 100 раз и до сих пор всегда возвращала одно и то же значение. Предполагается, что он также вернет это значение в 101-й раз, когда вы его вызовете.

Допустим, у нас есть следующая функция sum, которая всегда вызывалась с числовыми значениями в качестве аргументов:

function js

Эта функция вернет число 3. В следующий раз, когда мы вызовем ее, engine предположит, что мы вызываем функцию снова с двумя числовыми значениями.

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

Например, в следующий раз, когда мы вызываем функцию, мы передаем строку вместо числа. Поскольку JavaScript динамически типизирован, мы можем сделать это без ошибок!

f2

В данном случае число 2 будет преобразовано в строку, а функция вместо этого вернет строку «12». Он возвращается к выполнению интерпретируемого байт-кода и обновлит "type feedback".

 Конечно, есть много частей движка, которые я мы не рассмотрели в этом посте (JS heap, стек вызовов и т.д.), О которых мы поговорим в следующих постах. Если вас интересует внутреннее устройство JavaScript. V8 является открытым исходным кодом и имеет отличную документацию о том, как он работает под капотом, ссылки:

V8 Docs || V8 Github || Chrome University 2018: Life Of A Script

0 150 01.05.2021 07:19

Комментарии:

Пожалуйста авторизируйтесь, чтобы получить возможность оставлять комментарии