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

Два типа языков программирования

Языки программирования в основном делятся на две категории: интерпретируемые языки и компилируемые языки.

В процессе интерпретации интерпретатор просматривает исходный код и выполняет его построчно.

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

Является ли JavaScript интерпретируемым языком?

В простом определении да.

В те дни, когда Брендан Эйх сформировал JavaScript в NetScape, код JavaScript выполнялся с использованием интерпретатора построчно.

Более того, скомпилированные языки могут быть напрямую предоставлены компилятору, который создаст двоичное представление (машинный код) исходного кода перед выполнением.

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

Но дни NetScape JavaScript давно прошли, и к настоящему времени JavaScript сильно развился и используется и обрабатывается самыми разными способами, чем когда-либо прежде.

Ответ в ошибках

В интерпретируемых языках ошибка в строке 5 не будет обнаружена до тех пор, пока не будут выполнены строки с 1 по 4.

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

Все скомпилированные языки разбираются, поэтому проанализированный язык уже как минимум на одном этапе компилируется.

Не зная внутренностей движков JavaScript, вы можете сделать вывод, что JavaScript анализируется, потому что он вызывает «ранние ошибки», такие как повторяющиеся имена параметров, о которых сообщается до выполнения кода.

Эти ошибки нельзя было распознать без процесса синтаксического анализа, результат такого процесса называется AST.

Краткий обзор AST

Как интерпретируемые, так и компилируемые языки анализируют исходный код, начиная с простого текста и заканчивая структурой данных, называемой AST (абстрактное синтаксическое дерево).

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

Позже AST используется для генерации машинного кода.

Если вы хотите увидеть, как ведут себя AST, вы можете изучить эту замечательную песочницу AST под названием AST Explorer.

Итак, код JavaScript анализируется, но…

Является ли JavaScript компилируемым языком?

Как мы видим, границы становятся тоньше, ответ здесь скорее да, чем нет.

В современных движках JavaScript, таких как движок Google V8, после анализа код AST будет преобразован (скомпилирован) в оптимизированную двоичную форму.

Двигатели JavaScript используют JIT-компилятор

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

Процесс компиляции запускается в мониторе (профайлере), монитор проверяет, сколько раз выполнялся код и какие типы использовались.

Если определенный сегмент кода используется более нескольких раз, он концептуально помечен как «теплый».

Если этот же сегмент кода активно используется, он концептуально помечен как «горячий».

Базовый компилятор

После запуска интерпретатора, когда определенный сегмент кода используется несколько раз (концептуально «теплый»), этот сегмент будет скомпилирован и оптимизирован базовым компилятором, после чего этот оптимизированный машинный код будет сохранен в памяти.

Оптимизирующий компилятор

Когда определенный сегмент кода используется еще больше (является концептуально «горячим»), монитор отправляет его оптимизирующему компилятору.

Чтобы еще больше повысить производительность, оптимизирующий компилятор делает предположения, используя данные, собранные монитором.

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

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

Но в JavaScript все может стать непредсказуемым, и на сотой итерации предположение может оказаться неверным. В этом случае оптимизирующий компилятор выгрузит оптимизированный сегмент и вернется к использованию менее оптимизированного сегмента из памяти. Этот процесс называется «деоптимизация».

Резюме и заключительные мысли

  • После выхода из IDE (и некоторых процессов сборки, таких как транспиляция Babel и связывание Webpack) преобразованный код JavaScript передается движку.
  • Механизм JavaScript анализирует исходный код и создает AST.
  • Механизм строит двоичное представление кода с использованием AST, которое затем уточняется JIT-компилятором.
  • Исполнение.

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

Ответ на наш вопрос, вероятно, зависит от индивидуальной реализации Javascript, но мы видим, что на практике и при обычном использовании таких движков, как Google V8, JavaScript можно назвать компилируемым языком.

Буду рад услышать ваше мнение в комментариях :)

Ресурсы



https://github.com/getify/You-Dont-Know-JS

https://blog.sessionstack.com/how-javascript-works-parsing-abstract-syntax-trees-asts-5-tips-on-how-to-minimize-parse-time-abfcf7e8a0c8

https://hacks.mozilla.org/2017/02/a-crash-course-in-just-in-time-jit-compilers/