Как фронтенд-программист, часто ли вы используете webpack, rollup, babel, eslint? Это инструменты упаковки, инструменты компиляции кода, инструменты проверки синтаксиса. Как они этого добились? Абстрактное синтаксическое дерево, представленное в этой статье, — это технология, которую они используют.
В этой статье нет непонятных теорий, нет больших участков кода, она начинается с нуля, и Xiaobai может прочитать ее без каких-либо препятствий. Прочитав эту статью, вы поймете основные принципы AST и способы ее использования.
предисловие
Что такое абстрактное синтаксическое дерево?
AST (Abstract Syntax Tree) — это абстрактная структура синтаксического дерева, формирующая исходный код. На рисунке ниже показана форма абстрактного синтаксического дерева фрагмента кода JavaScript.
В чем польза абстрактных синтаксических деревьев?
Подсказки об ошибках IDE, форматирование кода, подсветка кода, автодополнение кода и т. д.
JSLint, JSHint, ESLint проверяет код на ошибки или стиль и т.д.
Инструмент, который преобразует исходный код JavaScript в абстрактное синтаксическое дерево (AST), называется JS Parser.
Процесс разбора JS Parser состоит из двух частей.
Лексический анализ: разделите всю строку кода на наименьший массив синтаксических единиц.
Синтаксический анализ: установить и проанализировать взаимосвязь между грамматическими единицами на основе сегментации слов.
Общие парсеры AST
В первые дни были uglifyjs и esprima
Эспри, на основе эспримы, для эслинта
Желудь, который, как говорят, имеет лучшую производительность и меньший размер, чем эсприма.
Вавилон, из желудя, для вавилона
Babel-eslint, поддерживаемый командой babel, для использования с ESLint.
1.2 Лексический анализ
Грамматическая единица — это наименьшая единица с фактическим значением в проанализированной грамматике, которая представляет собой просто слово в естественном языке.
Синтаксические единицы в коде Javascript в основном включают следующее:
Ключевые слова: например, var, let, const и т. д.
Идентификатор: последовательные символы, не заключенные в кавычки, которые могут быть переменной, ключевыми словами, такими как if, else, или встроенными константами, такими как true и false.
Операторы: +, -, *, / и т.д.
Числа: такие как шестнадцатеричные, десятичные, восьмеричные и научные выражения и т. д.
Строка: потому что для компьютера содержимое строки будет участвовать в вычислении или отображении
пробелы: последовательные пробелы, разрывы строк, отступы и т. д.
Комментарий: строчный комментарий или блочный комментарий — это наименьшая неделимая синтаксическая единица.
Другие: фигурные скобки, круглые скобки, точки с запятой, двоеточия и т. д.
1.3 Синтаксический анализ
Объедините результаты сегментации слов, определите взаимосвязь между словами, определите окончательное значение выражения слов и создайте абстрактные синтаксические деревья.
1.4 Пример
Возьмите оператор присваивания в качестве примера, используйте esprima для разбора:
var a = 1;
Результат лексического анализа следующий: видно, что результатом сегментации слов является массив, каждый элемент которого является минимальной грамматической единицей:
Один и тот же код JavaScript, результаты AST, проанализированные разными парсерами, одинаковы, потому что все они ссылаются на одну и ту же спецификацию синтаксического анализа AST.
The Estree SpecСпецификация — это документ спецификации вывода JavaScript AST движком SpiderMonkey, предоставленный инженерами Mozilla. Вы также можете обратиться к:SpiderMonkey in MDN
2.AST in Babel
Содержание AST было представлено ранее, давайте посмотрим, как Babel использует AST.
Как работает Вавилон
Рабочий процесс Babel проходит через три этапа: анализ, преобразование, генерация.
этап синтаксического анализа, который преобразует исходный код в AST
Этап преобразования с использованием различных плагинов для преобразования кода
На этапе генерации инструмент генерации кода используется для преобразования AST в код.
Разбор - анализ
Babel использует @babel/parser для анализа кода, а входная строка кода js генерирует AST в соответствии со спецификацией ESTree.
Парсер, используемый Babel, называется babylon.
Трансформировать-трансформировать
Берет AST и проходит по нему, добавляя, обновляя и удаляя узлы по пути. Это также часть работы по интеграции надстройки Babel.
Babel предоставляет метод @babel/traverse (обход) для поддержания общего состояния дерева AST.Параметры метода — исходный AST и пользовательские правила преобразования, а возвращаемый результат — преобразованный AST.
Генератор - Генерировать
На этапе генерации кода окончательный (после ряда преобразований) AST преобразуется в код в строковой форме, а также создаются исходные карты.
Пройдите весь AST и создайте строки, которые могут представлять преобразованный код.
Babel использует @babel/generator для преобразования модифицированного AST в код Процесс генерации можно настроить на сжатие и удаление комментариев, а также поддерживает sourceMap.
3.Demo with esprima
Поняв принцип работы Babel, мы напишем демонстрацию в соответствии с тремя шагами Babel, чтобы углубить наше понимание AST.
Мы собираемся использовать esprima для имитации функциональности двух перекодировок:
Измените == на конгруэнтное ===
Изменить parseInt(a) на parseInt(a,10)
Код до конвертации, before.js:
function fun1(opt) {
if (opt.status == 1) {
console.log('1');
}
}
function fun2(age) {
if (parseInt(age) >= 18) {
console.log('2');
}
}
Ожидаемый преобразованный код, after.js:
function fun1(opt) {
if (opt.status === 1) {//==变成===
console.log('1');
}
}
function fun2(age) {
if (parseInt(age, 10) >= 18) {//parseInt(a)变成parseInt(a,10)
console.log('2');
}
}
Приступайте к работе, сначала познакомьтесь с набором инструментов
Хорошо, откройте файл after.js, чтобы убедиться, что он был успешно преобразован? Это то, что мы ожидали? Есть ли вавилонское чувство? Да, на самом деле, Babel тоже это делает, но его функция правила преобразования довольно сложна, потому что ей нужно учитывать различные условия синтаксиса JavaScript, а рабочая нагрузка огромна, что является ядром Babel.
Оглядываясь назад на написанную нами демонстрацию, мы видим, что она полностью следовала трем шагам Babel. Первый шаг разбора и третий шаг генерации очень просты, о предложении и говорить нечего. Основное внимание уделяется Transform, реализации функции правила преобразования.Некоторые люди могут спросить, откуда вы знаете, что функции преобразования toEqual и setParseInt должны быть написаны таким образом?
Хорошо, чтобы ответить на этот вопрос, давайте взглянем на AST до и после преобразования кода этих двух правил.
Хорошо, пока эта демонстрация должна быть полностью понята.
4. Вопросы для размышления
Увидев это, вы поняли принцип и использование AST. Давайте рассмотрим задачу и проверим результаты обучения.
Предположим, что a является объектом, var a = {b : 1}, тогда a.b или a['b'], что более эффективно?
Методы записи a.b и a['b'] часто используются всеми, возможно, вы не заметили, что между этими двумя методами записи будут различия в производительности. Фактически, некоторые люди проводили тесты, и разница в производительности между ними невелика, и a.b будет работать немного лучше, чем a['b']. Так почему же a.b работает немного лучше, чем a['b']?
На мой взгляд, a.b может напрямую анализировать b как атрибут a, а a['b'] может иметь еще один процесс оценки, потому что содержимое в [] может быть переменной или константой.
Это утверждение может показаться разумным, но так ли это? Есть ли какие-либо доказательства в поддержку этого утверждения?
Ну, чтобы объяснить эту проблему, мы можем начать только с двигателя V8.
Код js может работать на процессоре, в основном благодаря движку js.Движок V8 разработан Google и применяется к браузеру Chrome и nodejs.Это классический движок js. Как видно из рисунка выше, в движке V8 есть три основных этапа трансляции js из исходного кода в машинный код: Parser (AST) -> Ignition (Bytecode) -> TurboFan (Machine Code)
Парсер: отвечает за преобразование исходного кода JavaScript в абстрактное синтаксическое дерево (AST).
Зажигание: интерпретатор, интерпретатор, отвечает за преобразование AST в байт-код, интерпретацию и выполнение байт-кода; в то же время сбор информации, необходимой для TurboFan для оптимизации компиляции, такой как тип параметров функции
TurboFan: компилятор, компилятор, использует информацию о типах, собранную Ignitio, для преобразования байт-кода в оптимизированный ассемблерный код.
Парсер-AST парсер
Вы должны быть знакомы с парсером AST, который мы представили сегодня.
Зажигание - Интерпретатор
AST преобразуется в язык, похожий на ассемблер, внешне похожий на язык ассемблера, называемый байт-кодом. Этот язык не имеет ничего общего с процессором, и байт-код, сгенерированный на машинах с разными процессорами, одинаков.
Турбофан - Компилятор
Как мы все знаем, архитектура и набор инструкций каждого процессора различны, и соответствующий язык ассемблера будет другим. На этом этапе V8 анализирует байт-код на языке ассемблера, подходящем для разных процессоров для разных процессоров. V8 может поддерживать более дюжины языков ассемблера процессора.
Теперь давайте сравним разницу между a.b и a['b'] при анализе V8
Код теста для a.b выглядит следующим образом:
function test001() {
var a = { b: 1 };
console.log(a.b)
}
test001();
Тестовый код для a['b'] выглядит следующим образом:
function test002() {
var a = { b: 1 };
console.log(a['b'])
}
test002();
Сравнив байт-код двух, вы обнаружите, что они абсолютно одинаковы, а это означает, что нет никакой разницы в производительности между двумя методами записи при выполнении уровня байт-кода и ниже. На самом деле они разные, смотреть можно только вверх, а там только этап Parser. Давайте посмотрим на разницу между их AST.
Единственное отличие, которое мы обнаружили, — это атрибут «вычисляемый», a.b — ложь, a['b'] — истина, что указывает на то, что при синтаксическом анализе в AST у a['b'] на один вычислительный процесс больше, чем у a.b. Из этого мы заключаем, что небольшая разница между ними должна быть здесь. Что ж, улики найдены, теперь сомнений быть не должно.
окончание
Увидев это, вы не только понимаете соответствующие знания AST, но и знаете, как движок V8 парсит js-код. Если вы считаете, что эта статья вам полезна, пожалуйста, тоже лайкните, кстати, большое спасибо (поклон на 90 градусов).
Если вы хотите узнать больше о публикациях skFeTeam, вы можете нажатьздесь, спасибо~