更多部落格文章,歡迎 Star Github/Blog
JS 抽象語法樹 詳細講解請看姊妹篇:前端進階之 Javascript 抽象語法樹
Javascript 程式碼的解析(Parse )步驟分為兩個階段:詞法分析(Lexical Analysis)和 語法分析(Syntactic Analysis)。這個步驟接收程式碼並輸出 抽象語法樹,亦稱 AST。
隨著 Babel 的生態越來越完善,我們通常會使用 Babel 來幫助我們分析程式碼的解析過程。Babel 使用一個基於 ESTree 並修改過的 AST,它的核心說明文件可以在 [這裡](https://github. com/babel/babel/blob/master/doc/ast/spec. md) 找到。
在分析 Javascript 的 AST 過程中,藉助於工具 AST Explorer 能幫助我們對 AST 節點有一個更好的感性認識。
為了幫助大家更好的結合例項分析,瞭解核心的 Babylon AST node types 組成,這裡列舉了 13 個常用例子,並分別列出了對應的 AST 節點及詳細的 node types 解析。
以下所有的程式碼的 AST 全部基於 Babylon7
變數宣告
程式碼
let a = 'hello'
複製程式碼
AST
VariableDeclaration
變數宣告,kind
屬性表示是什麼型別的宣告,因為 ES6 引入了 const/let
。
declarations
表示宣告的多個描述,因為我們可以這樣:let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration {
type: "VariableDeclaration";
declarations: [ VariableDeclarator ];
kind: "var";
}
複製程式碼
VariableDeclarator
變數宣告的描述,id
表示變數名稱節點,init
表示初始值的表示式,可以為 null
。
interface VariableDeclarator <: Node {
type: "VariableDeclarator";
id: Pattern;
init: Expression | null;
}
複製程式碼
Identifier
識別符號,我覺得應該是這麼叫的,就是我們寫 JS 時自定義的名稱,如變數名,函式名,屬性名,都歸為識別符號。相應的介面是這樣的:
interface Identifier <: Expression, Pattern {
type: "Identifier";
name: string;
}
複製程式碼
一個識別符號可能是一個表示式,或者是解構的模式(ES6 中的解構語法)。我們等會會看到 Expression
和 Pattern
相關的內容的。
Literal
字面量,這裡不是指 []
或者 {}
這些,而是本身語義就代表了一個值的字面量,如 1
,“hello”
, true
這些,還有正規表示式(有一個擴充套件的 Node
來表示正規表示式),如 /\d?/
。我們看一下文件的定義:
interface Literal <: Expression {
type: "Literal";
value: string | boolean | null | number | RegExp;
}
複製程式碼
value
這裡即對應了字面量的值,我們可以看出字面量值的型別,字串,布林,數值,null
和正則。
二元運算表示式
程式碼
let a = 3+4
複製程式碼
AST
BinaryExpression
二元運算表示式節點,left
和 right
表示運算子左右的兩個表示式,operator
表示一個二元運算子。
interface BinaryExpression <: Expression {
type: "BinaryExpression";
operator: BinaryOperator;
left: Expression;
right: Expression;
}
複製程式碼
BinaryOperator
二元運算子,所有值如下:
enum BinaryOperator {
"==" | "!=" | "===" | "!=="
| "<" | "<=" | ">" | ">="
| "<<" | ">>" | ">>>"
| "+" | "-" | "*" | "/" | "%"
| "|" | "^" | "&" | "in"
| "instanceof"
}
複製程式碼
賦值表示式
程式碼
這個例子會稍微複雜一點,涉及到的 Node 型別比較多。
this.state = {date: new Date()};
複製程式碼
AST
ExpressionStatement
表示式語句節點,a = a + 1
或者 a++
裡邊會有一個 expression
屬性指向一個表示式節點物件(後邊會提及表示式)。
interface ExpressionStatement <: Statement {
type: "ExpressionStatement";
expression: Expression;
}
複製程式碼
AssignmentExpression
賦值表示式節點,operator
屬性表示一個賦值運算子,left
和 right
是賦值運算子左右的表示式。
interface AssignmentExpression <: Expression {
type: "AssignmentExpression";
operator: AssignmentOperator;
left: Pattern | Expression;
right: Expression;
}
複製程式碼
AssignmentOperator
賦值運算子,所有值如下:(常用的並不多)
enum AssignmentOperator {
"=" | "+=" | "-=" | "*=" | "/=" | "%="
| "<<=" | ">>=" | ">>>="
| "|=" | "^=" | "&="
}
複製程式碼
MemberExpression
成員表示式節點,即表示引用物件成員的語句,object
是引用物件的表示式節點,property
是表示屬性名稱,computed
如果為 false
,是表示 .
來引用成員,property
應該為一個 Identifier
節點,如果 computed
屬性為 true
,則是 []
來進行引用,即 property
是一個 Expression
節點,名稱是表示式的結果值。
interface MemberExpression <: Expression, Pattern {
type: "MemberExpression";
object: Expression;
property: Expression;
computed: boolean;
}
複製程式碼
ThisExpression
表示 this
。
interface ThisExpression <: Expression {
type: "ThisExpression";
}
複製程式碼
ObjectExpression
物件表示式節點,property
屬性是一個陣列,表示物件的每一個鍵值對,每一個元素都是一個屬性節點。
interface ObjectExpression <: Expression {
type: "ObjectExpression";
properties: [ Property ];
}
複製程式碼
Property
物件表示式中的屬性節點。key
表示鍵,value
表示值,由於 ES5 語法中有 get/set
的存在,所以有一個 kind
屬性,用來表示是普通的初始化,或者是 get/set
。
interface Property <: Node {
type: "Property";
key: Literal | Identifier;
value: Expression;
kind: "init" | "get" | "set";
}
複製程式碼
NewExpression
new
表示式。
interface NewExpression <: CallExpression {
type: "NewExpression";
}
複製程式碼
函式呼叫表示式
程式碼
console.log(`Hello ${name}`)
複製程式碼
AST
CallExpression
函式呼叫表示式,即表示了 func(1, 2)
這一型別的語句。callee
屬性是一個表示式節點,表示函式,arguments
是一個陣列,元素是表示式節點,表示函式引數列表。
interface CallExpression <: Expression {
type: "CallExpression";
callee: Expression;
arguments: [ Expression ];
}
複製程式碼
TemplateLiteral
interface TemplateLiteral <: Expression {
type: "TemplateLiteral";
quasis: [ TemplateElement ];
expressions: [ Expression ];
}
複製程式碼
TemplateElement
interface TemplateElement <: Node {
type: "TemplateElement";
tail: boolean;
value: {
cooked: string | null;
raw: string;
};
}
複製程式碼
箭頭函式
程式碼
i => i++
複製程式碼
AST
ArrowFunctionExpression
箭頭函式表示式。
interface ArrowFunctionExpression <: Function, Expression {
type: "ArrowFunctionExpression";
body: BlockStatement | Expression;
expression: boolean;
}
複製程式碼
UpdateExpression
update 運算表示式節點,即 ++/--
,和一元運算子類似,只是 operator
指向的節點物件型別不同,這裡是 update 運算子。
interface UpdateExpression <: Expression {
type: "UpdateExpression";
operator: UpdateOperator;
argument: Expression;
prefix: boolean;
}
複製程式碼
UpdateOperator
update 運算子,值為 ++
或 --
,配合 update 表示式節點的 prefix
屬性來表示前後。
enum UpdateOperator {
"++" | "--"
}
複製程式碼
函式宣告
程式碼
function Hello(name = 'Lily'){
}
複製程式碼
AST
FunctionDeclaration
函式宣告,和之前提到的 Function 不同的是,id
不能為 null
。
interface FunctionDeclaration <: Function, Declaration {
type: "FunctionDeclaration";
id: Identifier;
}
複製程式碼
AssignmentPattern
interface AssignmentPattern <: Pattern {
type: "AssignmentPattern";
left: Pattern;
right: Expression;
}
複製程式碼
BlockStatement
塊語句節點,舉個例子:if (...) { // 這裡是塊語句的內容 }
,塊裡邊可以包含多個其他的語句,所以有一個 body
屬性,是一個陣列,表示了塊裡邊的多個語句。
interface BlockStatement <: Statement {
type: "BlockStatement";
body: [ Statement ];
}
複製程式碼
類宣告
程式碼
class Clock extends Component{
render(){
}
}
複製程式碼
AST
Classes
interface Class <: Node {
id: Identifier | null;
superClass: Expression | null;
body: ClassBody;
decorators: [ Decorator ];
}
複製程式碼
ClassBody
interface ClassBody <: Node {
type: "ClassBody";
body: [ ClassMethod | ClassPrivateMethod | ClassProperty | ClassPrivateProperty ];
}
複製程式碼
ClassMethod
interface ClassMethod <: Function {
type: "ClassMethod";
key: Expression;
kind: "constructor" | "method" | "get" | "set";
computed: boolean;
static: boolean;
decorators: [ Decorator ];
}
複製程式碼
if 語句
程式碼
if(a === 0){
}
複製程式碼
AST
IfStatement
if
語句節點,很常見,會帶有三個屬性,test
屬性表示 if (...)
括號中的表示式。
consequent
屬性是表示條件為 true
時的執行語句,通常會是一個塊語句。
alternate
屬性則是用來表示 else
後跟隨的語句節點,通常也會是塊語句,但也可以又是一個 if
語句節點,即類似這樣的結構:
if (a) { //... } else if (b) { // ... }
。
alternate
當然也可以為 null
。
interface IfStatement <: Statement {
type: "IfStatement";
test: Expression;
consequent: Statement;
alternate: Statement | null;
}
複製程式碼
switch 語句
程式碼
switch(num){
case 0:
x = 'Sunday'
break;
default:
x = 'Weekday'
}
複製程式碼
AST
SwitchStatement
switch
語句節點,有兩個屬性,discriminant
屬性表示 switch
語句後緊隨的表示式,通常會是一個變數,cases
屬性是一個 case
節點的陣列,用來表示各個 case
語句。
interface SwitchStatement <: Statement {
type: "SwitchStatement";
discriminant: Expression;
cases: [ SwitchCase ];
}
複製程式碼
SwitchCase
switch
的 case
節點。test
屬性代表這個 case
的判斷表示式,consequent
則是這個 case
的執行語句。
當 test
屬性是 null
時,則是表示 default
這個 case
節點。
interface SwitchCase <: Node {
type: "SwitchCase";
test: Expression | null;
consequent: [ Statement ];
}
複製程式碼
for 語句
程式碼
for (var i = 0; i < 9; i++) {
}
複製程式碼
AST
ForStatement
for
迴圈語句節點,屬性 init/test/update
分別表示了 for
語句括號中的三個表示式,初始化值,迴圈判斷條件,每次迴圈執行的變數更新語句(init
可以是變數宣告或者表示式)。這三個屬性都可以為 null
,即 for(;;){}
。
body
屬性用以表示要迴圈執行的語句。
interface ForStatement <: Statement {
type: "ForStatement";
init: VariableDeclaration | Expression | null;
test: Expression | null;
update: Expression | null;
body: Statement;
}
複製程式碼
模組引入
程式碼
import React from 'react'
複製程式碼
AST
ImportDeclaration
模組宣告。
interface ImportDeclaration <: ModuleDeclaration {
type: "ImportDeclaration";
specifiers: [ ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier ];
source: Literal;
}
複製程式碼
ImportDefaultSpecifier
interface ImportDefaultSpecifier <: ModuleSpecifier {
type: "ImportDefaultSpecifier";
}
複製程式碼
模組匯出
程式碼
export default Clock
複製程式碼
AST
ExportDefaultDeclaration
interface OptFunctionDeclaration <: FunctionDeclaration {
id: Identifier | null;
}
interface OptClasDeclaration <: ClassDeclaration {
id: Identifier | null;
}
interface ExportDefaultDeclaration <: ModuleDeclaration {
type: "ExportDefaultDeclaration";
declaration: OptFunctionDeclaration | OptClassDeclaration | Expression;
}
複製程式碼
JSX render 方法
程式碼:
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
複製程式碼