Babel爲當前最流行的代碼JavaScript編譯器了,其使用的JavaScript解析器爲babel-parser,最初是從Acorn
項目fork
出來的。Acorn 很是快,易於使用,而且針對非標準特性(以及那些將來的標準特性) 設計了一個基於插件的架構。本文主要介紹esprima
解析生成的抽象語法樹節點,esprima
的實現也是基於Acorn的。javascript
原文地址html
JavaScript Parser 是把js源碼轉化爲抽象語法樹(AST)的解析器。這個步驟分爲兩個階段:詞法分析(Lexical Analysis) 和 語法分析(Syntactic Analysis)。前端
經常使用的JavaScript Parser:java
詞法分析階段把字符串形式的代碼轉換爲 令牌(tokens)流。你能夠把令牌看做是一個扁平的語法片斷數組。jquery
n * n;
例如上面n*n
的詞法分析獲得結果以下:webpack
[ { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } }, { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } }, { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } }, ]
每個 type 有一組屬性來描述該令牌:git
{ type: { label: 'name', keyword: undefined, beforeExpr: false, startsExpr: true, rightAssociative: false, isLoop: false, isAssign: false, prefix: false, postfix: false, binop: null, updateContext: null }, ... }
和 AST 節點同樣它們也有 start,end,loc 屬性。github
語法分析就是根據詞法分析的結果,也就是令牌tokens,將其轉換成AST。web
function square(n) { return n * n; }
如上面代碼,生成的AST結構以下:正則表達式
{ type: "FunctionDeclaration", id: { type: "Identifier", name: "square" }, params: [{ type: "Identifier", name: "n" }], body: { type: "BlockStatement", body: [{ type: "ReturnStatement", argument: { type: "BinaryExpression", operator: "*", left: { type: "Identifier", name: "n" }, right: { type: "Identifier", name: "n" } } }] } }
下文將對AST各個類型節點作解釋。更多AST生成,入口以下:
結合可視化工具,舉個例子
以下代碼:
var a = 42; var b = 5; function addA(d) { return a + d; } var c = addA(2) + b;
第一步詞法分析以後長成以下圖所示:
語法分析,生產抽象語法樹,生成的抽象語法樹以下圖所示
全部節點類型都實現如下接口:
interface Node { type: string; range?: [number, number]; loc?: SourceLocation; }
該type字段是表示AST變體類型的字符串。該loc字段表示節點的源位置信息。若是解析器沒有生成有關節點源位置的信息,則該字段爲null;不然它是一個對象,包括一個起始位置(被解析的源區域的第一個字符的位置)和一個結束位置.
interface SourceLocation { start: Position; end: Position; source?: string | null; }
每一個Position對象由一個line數字(1索引)和一個column數字(0索引)組成:
interface Position { line: uint32 >= 1; column: uint32 >= 0; }
interface Program <: Node { type: "Program"; sourceType: 'script' | 'module'; body: StatementListItem[] | ModuleItem[]; }
表示一個完整的源代碼樹。
源代碼數的來源包括兩種,一種是script腳本,一種是modules模塊
當爲script時,body爲StatementListItem
。
當爲modules時,body爲ModuleItem
。
類型StatementListItem
和ModuleItem
類型以下。
type StatementListItem = Declaration | Statement; type ModuleItem = ImportDeclaration | ExportDeclaration | StatementListItem;
import語法,導入模塊
type ImportDeclaration { type: 'ImportDeclaration'; specifiers: ImportSpecifier[]; source: Literal; }
ImportSpecifier
類型以下:
interface ImportSpecifier { type: 'ImportSpecifier' | 'ImportDefaultSpecifier' | 'ImportNamespaceSpecifier'; local: Identifier; imported?: Identifier; }
ImportSpecifier
語法以下:
import { foo } from './foo';
ImportDefaultSpecifier
語法以下:
import foo from './foo';
ImportNamespaceSpecifier
語法以下
import * as foo from './foo';
export類型以下
type ExportDeclaration = ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration;
ExportAllDeclaration
從指定模塊中導出
interface ExportAllDeclaration { type: 'ExportAllDeclaration'; source: Literal; }
語法以下:
export * from './foo';
ExportDefaultDeclaration
導出默認模塊
interface ExportDefaultDeclaration { type: 'ExportDefaultDeclaration'; declaration: Identifier | BindingPattern | ClassDeclaration | Expression | FunctionDeclaration; }
語法以下:
export default 'foo';
ExportNamedDeclaration
導出部分模塊
interface ExportNamedDeclaration { type: 'ExportNamedDeclaration'; declaration: ClassDeclaration | FunctionDeclaration | VariableDeclaration; specifiers: ExportSpecifier[]; source: Literal; }
語法以下:
export const foo = 'foo';
declaration
,即聲明,類型以下:
type Declaration = VariableDeclaration | FunctionDeclaration | ClassDeclaration;
statements
,即語句,類型以下:
type Statement = BlockStatement | BreakStatement | ContinueStatement | DebuggerStatement | DoWhileStatement | EmptyStatement | ExpressionStatement | ForStatement | ForInStatement | ForOfStatement | FunctionDeclaration | IfStatement | LabeledStatement | ReturnStatement | SwitchStatement | ThrowStatement | TryStatement | VariableDeclaration | WhileStatement | WithStatement;
變量聲明,kind 屬性表示是什麼類型的聲明,由於 ES6 引入了 const/let。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var" | "let" | "const"; }
函數聲明(非函數表達式)
interface FunctionDeclaration { type: 'FunctionDeclaration'; id: Identifier | null; params: FunctionParameter[]; body: BlockStatement; generator: boolean; async: boolean; expression: false; }
例如:
function foo() {} function *bar() { yield "44"; } async function noop() { await new Promise(function(resolve, reject) { resolve('55'); }) }
類聲明(非類表達式)
interface ClassDeclaration { type: 'ClassDeclaration'; id: Identifier | null; superClass: Identifier | null; body: ClassBody; }
ClassBody
聲明以下:
interface ClassBody { type: 'ClassBody'; body: MethodDefinition[]; }
MethodDefinition
表示方法聲明;
interface MethodDefinition { type: 'MethodDefinition'; key: Expression | null; computed: boolean; value: FunctionExpression | null; kind: 'method' | 'constructor'; static: boolean; }
class foo { constructor() {} method() {} };
continue語句
interface ContinueStatement { type: 'ContinueStatement'; label: Identifier | null; }
例如:
for (var i = 0; i < 10; i++) { if (i === 0) { continue; } }
debugger語句
interface DebuggerStatement { type: 'DebuggerStatement'; }
例如
while(true) { debugger; }
do-while語句
interface DoWhileStatement { type: 'DoWhileStatement'; body: Statement; test: Expression; }
test
表示while條件
例如:
var i = 0; do { i++; } while(i = 2)
空語句
interface EmptyStatement { type: 'EmptyStatement'; }
例如:
if(true); var a = []; for(i = 0; i < a.length; a[i++] = 0);
表達式語句,即,由單個表達式組成的語句。
interface ExpressionStatement { type: 'ExpressionStatement'; expression: Expression; directive?: string; }
當表達式語句表示一個指令(例如「use strict」)時,directive屬性將包含該指令字符串。
例如:
(function(){});
for語句
interface ForStatement { type: 'ForStatement'; init: Expression | VariableDeclaration | null; test: Expression | null; update: Expression | null; body: Statement; }
for...in語句
interface ForInStatement { type: 'ForInStatement'; left: Expression; right: Expression; body: Statement; each: false; }
for...of語句
interface ForOfStatement { type: 'ForOfStatement'; left: Expression; right: Expression; body: Statement; }
if 語句
interface IfStatement { type: 'IfStatement'; test: Expression; consequent: Statement; alternate?: Statement; }
consequent
表示if命中後內容,alternate
表示else或者else if的內容。
label語句,多用於精確的使用嵌套循環中的continue和break。
interface LabeledStatement { type: 'LabeledStatement'; label: Identifier; body: Statement; }
如:
var num = 0; outPoint: for (var i = 0 ; i < 10 ; i++){ for (var j = 0 ; j < 10 ; j++){ if( i == 5 && j == 5 ){ break outPoint; } num++; } }
return 語句
interface ReturnStatement { type: 'ReturnStatement'; argument: Expression | null; }
Switch語句
interface SwitchStatement { type: 'SwitchStatement'; discriminant: Expression; cases: SwitchCase[]; }
discriminant
表示switch的變量。
SwitchCase
類型以下
interface SwitchCase { type: 'SwitchCase'; test: Expression | null; consequent: Statement[]; }
throw語句
interface ThrowStatement { type: 'ThrowStatement'; argument: Expression; }
try...catch語句
interface TryStatement { type: 'TryStatement'; block: BlockStatement; handler: CatchClause | null; finalizer: BlockStatement | null; }
handler
爲catch處理聲明內容,finalizer
爲finally內容。
CatchClaus
類型以下
interface CatchClause { type: 'CatchClause'; param: Identifier | BindingPattern; body: BlockStatement; }
例如:
try { foo(); } catch (e) { console.erroe(e); } finally { bar(); }
while語句
interface WhileStatement { type: 'WhileStatement'; test: Expression; body: Statement; }
test
爲斷定表達式
with語句(指定塊語句的做用域的做用域)
interface WithStatement { type: 'WithStatement'; object: Expression; body: Statement; }
如:
var a = {}; with(a) { name = 'xiao.ming'; } console.log(a); // {name: 'xiao.ming'}
Expressions
可用類型以下:
type Expression = ThisExpression | Identifier | Literal | ArrayExpression | ObjectExpression | FunctionExpression | ArrowFunctionExpression | ClassExpression | TaggedTemplateExpression | MemberExpression | Super | MetaProperty | NewExpression | CallExpression | UpdateExpression | AwaitExpression | UnaryExpression | BinaryExpression | LogicalExpression | ConditionalExpression | YieldExpression | AssignmentExpression | SequenceExpression;
Patterns
可用有兩種類型,函數模式和對象模式以下:
type BindingPattern = ArrayPattern | ObjectPattern;
this
表達式
interface ThisExpression { type: 'ThisExpression'; }
標識符,就是咱們寫 JS 時自定義的名稱,如變量名,函數名,屬性名,都歸爲標識符。相應的接口是這樣的:
interface Identifier { type: 'Identifier'; name: string; }
字面量,這裏不是指 [] 或者 {} 這些,而是自己語義就表明了一個值的字面量,如 1,「hello」, true 這些,還有正則表達式(有一個擴展的 Node 來表示正則表達式),如 /d?/。
interface Literal { type: 'Literal'; value: boolean | number | string | RegExp | null; raw: string; regex?: { pattern: string, flags: string }; }
例如:
var a = 1; var b = 'b'; var c = false; var d = /\d/;
數組表達式
interface ArrayExpression { type: 'ArrayExpression'; elements: ArrayExpressionElement[]; }
例:
[1, 2, 3, 4];
數組表達式的節點,類型以下
type ArrayExpressionElement = Expression | SpreadElement;
Expression包含全部表達式,SpreadElement爲擴展運算符語法。
擴展運算符
interface SpreadElement { type: 'SpreadElement'; argument: Expression; }
如:
var a = [3, 4]; var b = [1, 2, ...a]; var c = {foo: 1}; var b = {bar: 2, ...c};
對象表達式
interface ObjectExpression { type: 'ObjectExpression'; properties: Property[]; }
Property
表明爲對象的屬性描述
類型以下
interface Property { type: 'Property'; key: Expression; computed: boolean; value: Expression | null; kind: 'get' | 'set' | 'init'; method: false; shorthand: boolean; }
kind
用來表示是普通的初始化,或者是 get/set。
例如:
var obj = { foo: 'foo', bar: function() {}, noop() {}, // method 爲 true ['computed']: 'computed' // computed 爲 true }
函數表達式
interface FunctionExpression { type: 'FunctionExpression'; id: Identifier | null; params: FunctionParameter[]; body: BlockStatement; generator: boolean; async: boolean; expression: boolean; }
例如:
var foo = function () {}
箭頭函數表達式
interface ArrowFunctionExpression { type: 'ArrowFunctionExpression'; id: Identifier | null; params: FunctionParameter[]; body: BlockStatement | Expression; generator: boolean; async: boolean; expression: false; }
generator
表示是否爲generator函數,async
表示是否爲async/await函數,params
爲參數定義。
FunctionParameter
類型以下
type FunctionParameter = AssignmentPattern | Identifier | BindingPattern;
例:
var foo = () => {};
類表達式
interface ClassExpression { type: 'ClassExpression'; id: Identifier | null; superClass: Identifier | null; body: ClassBody; }
例如:
var foo = class { constructor() {} method() {} };
標記模板文字函數
interface TaggedTemplateExpression { type: 'TaggedTemplateExpression'; readonly tag: Expression; readonly quasi: TemplateLiteral; }
TemplateLiteral
類型以下
interface TemplateLiteral { type: 'TemplateLiteral'; quasis: TemplateElement[]; expressions: Expression[]; }
TemplateElement
類型以下
interface TemplateElement { type: 'TemplateElement'; value: { cooked: string; raw: string }; tail: boolean; }
例如
var foo = function(a){ console.log(a); } foo`test`;
屬性成員表達式
interface MemberExpression { type: 'MemberExpression'; computed: boolean; object: Expression; property: Expression; }
例如:
const foo = {bar: 'bar'}; foo.bar; foo['bar']; // computed 爲 true
父類關鍵字
interface Super { type: 'Super'; }
例如:
class foo {}; class bar extends foo { constructor() { super(); } }
(這個不知道幹嗎用的)
interface MetaProperty { type: 'MetaProperty'; meta: Identifier; property: Identifier; }
例如:
new.target // 經過new 聲明的對象,new.target會存在 import.meta
函數執行表達式
interface CallExpression { type: 'CallExpression'; callee: Expression | Import; arguments: ArgumentListElement[]; }
Import類型,沒搞懂。
interface Import { type: 'Import' }
ArgumentListElement
類型
type ArgumentListElement = Expression | SpreadElement;
如:
var foo = function (){}; foo();
new 表達式
interface NewExpression { type: 'NewExpression'; callee: Expression; arguments: ArgumentListElement[]; }
更新操做符表達式,如++
、--
;
interface UpdateExpression { type: "UpdateExpression"; operator: '++' | '--'; argument: Expression; prefix: boolean; }
如:
var i = 0; i++; ++i; // prefix爲true
await表達式,會與async連用。
interface AwaitExpression { type: 'AwaitExpression'; argument: Expression; }
如
async function foo() { var bar = function() { new Primise(function(resolve, reject) { setTimeout(function() { resove('foo') }, 1000); }); } return await bar(); } foo() // foo
一元操做符表達式
interface UnaryExpression { type: "UnaryExpression"; operator: UnaryOperator; prefix: boolean; argument: Expression; }
枚舉UnaryOperator
enum UnaryOperator { "-" | "+" | "!" | "~" | "typeof" | "void" | "delete" | "throw" }
二元操做符表達式
interface BinaryExpression { type: 'BinaryExpression'; operator: BinaryOperator; left: Expression; right: Expression; }
枚舉BinaryOperator
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "**" | "|" | "^" | "&" | "in" | "instanceof" | "|>" }
邏輯運算符表達式
interface LogicalExpression { type: 'LogicalExpression'; operator: '||' | '&&'; left: Expression; right: Expression; }
如:
var a = '-'; var b = a || '-'; if (a && b) {}
條件運算符
interface ConditionalExpression { type: 'ConditionalExpression'; test: Expression; consequent: Expression; alternate: Expression; }
例如:
var a = true; var b = a ? 'consequent' : 'alternate';
yield表達式
interface YieldExpression { type: 'YieldExpression'; argument: Expression | null; delegate: boolean; }
例如:
function* gen(x) { var y = yield x + 2; return y; }
賦值表達式。
interface AssignmentExpression { type: 'AssignmentExpression'; operator: '=' | '*=' | '**=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|='; left: Expression; right: Expression; }
operator
屬性表示一個賦值運算符,left
和right
是賦值運算符左右的表達式。
序列表達式(使用逗號)。
interface SequenceExpression { type: 'SequenceExpression'; expressions: Expression[]; }
var a, b; a = 1, b = 2
數組解析模式
interface ArrayPattern { type: 'ArrayPattern'; elements: ArrayPatternElement[]; }
例:
const [a, b] = [1,3];
elements表明數組節點
ArrayPatternElement以下
type ArrayPatternElement = AssignmentPattern | Identifier | BindingPattern | RestElement | null;
默認賦值模式,數組解析、對象解析、函數參數默認值使用。
interface AssignmentPattern { type: 'AssignmentPattern'; left: Identifier | BindingPattern; right: Expression; }
例:
const [a, b = 4] = [1,3];
剩餘參數模式,語法與擴展運算符相近。
interface RestElement { type: 'RestElement'; argument: Identifier | BindingPattern; }
例:
const [a, b, ...c] = [1, 2, 3, 4];
對象解析模式
interface ObjectPattern { type: 'ObjectPattern'; properties: Property[]; }
例:
const object = {a: 1, b: 2}; const { a, b } = object;
AST的做用大體分爲幾類
瞭解AST,最終仍是爲了讓咱們瞭解咱們使用的工具,固然也讓咱們更瞭解JavaScript,更靠近JavaScript。