13 個示例快速入門 JS 抽象語法樹

PinesCheng發表於2019-03-03

更多部落格文章,歡迎 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

image

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 中的解構語法)。我們等會會看到 ExpressionPattern 相關的內容的。

Literal

字面量,這裡不是指 [] 或者 {} 這些,而是本身語義就代表了一個值的字面量,如 1“hello”, true 這些,還有正規表示式(有一個擴充套件的 Node 來表示正規表示式),如 /d?/。我們看一下文件的定義:

interface Literal <: Expression {
    type: "Literal";
    value: string | boolean | null | number | RegExp;
}
複製程式碼

value 這裡即對應了字面量的值,我們可以看出字面量值的型別,字串,布林,數值,null 和正則。

二元運算表示式

程式碼

let a = 3+4
複製程式碼

AST

image

BinaryExpression

二元運算表示式節點,leftright 表示運算子左右的兩個表示式,operator 表示一個二元運算子。

interface BinaryExpression <: Expression {
    type: "BinaryExpression";
    operator: BinaryOperator;
    left: Expression;
    right: Expression;
}
複製程式碼

BinaryOperator

二元運算子,所有值如下:

enum BinaryOperator {
    "==" | "!=" | "===" | "!=="
         | "<" | "<=" | ">" | ">="
         | "<<" | ">>" | ">>>"
         | "+" | "-" | "*" | "/" | "%"
         | "|" | "^" | "&" | "in"
         | "instanceof"
}
複製程式碼

賦值表示式

程式碼

這個例子會稍微複雜一點,涉及到的 Node 型別比較多。

    this.state = {date: new Date()};
複製程式碼

AST

image

ExpressionStatement

表示式語句節點,a = a + 1 或者 a++ 裡邊會有一個 expression 屬性指向一個表示式節點物件(後邊會提及表示式)。

interface ExpressionStatement <: Statement {
    type: "ExpressionStatement";
    expression: Expression;
}
複製程式碼

AssignmentExpression

賦值表示式節點,operator 屬性表示一個賦值運算子,leftright 是賦值運算子左右的表示式。

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

image

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

image

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

image

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

image

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

image

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

image

SwitchStatement

switch 語句節點,有兩個屬性,discriminant 屬性表示 switch 語句後緊隨的表示式,通常會是一個變數,cases 屬性是一個 case 節點的陣列,用來表示各個 case 語句。

interface SwitchStatement <: Statement {
    type: "SwitchStatement";
    discriminant: Expression;
    cases: [ SwitchCase ];
}
複製程式碼

SwitchCase

switchcase 節點。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

13 個示例快速入門 JS 抽象語法樹

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

13 個示例快速入門 JS 抽象語法樹

ImportDeclaration

模組宣告。

interface ImportDeclaration <: ModuleDeclaration {
  type: "ImportDeclaration";
  specifiers: [ ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier ];
  source: Literal;
}
複製程式碼

ImportDefaultSpecifier

interface ImportDefaultSpecifier <: ModuleSpecifier {
  type: "ImportDefaultSpecifier";
}
複製程式碼

模組匯出

程式碼

export default Clock
複製程式碼

AST

image

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>
    );
  }
複製程式碼

AST

13 個示例快速入門 JS 抽象語法樹

參考

相關文章