更多博客文章,歡迎 Star Github/Blog
Babel 是 JavaScript 編譯器 compiler,更確切地說是源碼到源碼的編譯器,一般也叫作 轉換編譯器(transpiler)
。 意思是說你爲 Babel 提供一些 JavaScript 代碼,Babel 更改這些代碼,而後返回給你新生成的代碼。node
Babel 是一個通用的多功能的 JavaScript 編譯器。此外它還擁有衆多模塊可用於不一樣形式的靜態分析。靜態分析是在不須要執行代碼的前提下對代碼進行分析的處理過程 (執行代碼的同時進行代碼分析便是動態分析)。 靜態分析的目的是多種多樣的, 它可用於語法檢查,編譯,代碼高亮,代碼轉換,優化,壓縮等等場景。git
Babylon 是 Babel 的解析器 parser。最初是 從 Acorn 項目 fork 出來的。Acorn 很是快,易於使用,而且針對非標準特性(以及那些將來的標準特性) 設計了一個基於插件的架構。github
Babylon 已經移入 Babel mono-repo 改名爲 babel-parser
首先,讓咱們安裝它。正則表達式
$ npm install --save babylon
先從解析一個代碼字符串開始:shell
import * as babylon from "babylon"; const code = `function square(n) { return n * n; }`; babylon.parse(code); // Node { // type: "File", // start: 0, // end: 38, // loc: SourceLocation {...}, // program: Node {...}, // comments: [], // tokens: [...] // }
咱們還能像下面這樣傳遞選項給 parse()
方法:express
babylon.parse(code, { sourceType: "module", // default: "script" plugins: ["jsx"] // default: [] });
sourceType
能夠是 "module"
或者 "script"
,它表示 Babylon 應該用哪一種模式來解析。 "module"
將會在嚴格模式下解析而且容許模塊定義,"script"
則不會。npm
注意:sourceType
的默認值是"script"
而且在發現import
或export
時產生錯誤。 使用scourceType: "module"
來避免這些錯誤。
因爲 Babylon 使用了基於插件的架構,所以有一個 plugins
選項能夠開關內置的插件。 注意 Babylon 還沒有對外部插件開放此 API 接口,不排除將來會開放此API。json
解析(Parse )步驟接收代碼並輸出 抽象語法樹(AST)
。 這個步驟分爲兩個階段:詞法分析(Lexical Analysis) 和 語法分析(Syntactic Analysis)。數組
詞法分析階段把字符串形式的代碼轉換爲 令牌(tokens) 流。babel
你能夠把令牌看做是一個扁平的語法片斷數組:
n * n;
[ { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } }, { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } }, { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } }, ... ]
每個 type
有一組屬性來描述該令牌:
{ 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
屬性。
語法分析階段會把一個令牌流轉換成 抽象語法樹(AST)
的形式。 這個階段會使用令牌中的信息把它們轉換成一個 AST 的表述結構,這樣更易於後續的操做。
這個處理過程當中的每一步都涉及到建立或是操做抽象語法樹,亦稱 AST。
Babel 使用一個基於 ESTree 並修改過的 AST,它的內核說明文檔能夠在 這裏. com/babel/babel/blob/master/doc/ast/spec. md)找到。
function square(n) { return n * n; }
AST Explorer 可讓你對 AST 節點有一個更好的感性認識。 這裏是上述代碼的一個示例連接。
這個程序能夠被表示成以下所示的 JavaScript Object(對象):
{ 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 的每一層都擁有相同的結構:
{ type: "FunctionDeclaration", id: {...}, params: [...], body: {...} }
{ type: "Identifier", name: ... }
{ type: "BinaryExpression", operator: ..., left: {...}, right: {...} }
注意:出於簡化的目的移除了某些屬性
這樣的每一層結構也被叫作 節點(Node)。 一個 AST 能夠由單一的節點或是成百上千個節點構成。 它們組合在一塊兒能夠描述用於靜態分析的程序語法。
每個節點都有以下所示的接口(Interface):
interface Node { type: string; }
字符串形式的 type
字段表示節點的類型(如: "FunctionDeclaration"
,"Identifier"
,或 "BinaryExpression"
)。 每一種類型的節點定義了一些附加屬性用來進一步描述該節點類型。
Babel 還爲每一個節點額外生成了一些屬性,用於描述該節點在原始代碼中的位置。
{ type: ..., start: 0, end: 38, loc: { start: { line: 1, column: 0 }, end: { line: 3, column: 1 } }, ... }
每個節點都會有 start
,end
,loc
這幾個屬性。
let a = 'hello'
變量聲明,kind
屬性表示是什麼類型的聲明,由於 ES6 引入了 const/let
。declarations
表示聲明的多個描述,由於咱們能夠這樣:let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var"; }
變量聲明的描述,id
表示變量名稱節點,init
表示初始值的表達式,能夠爲 null
。
interface VariableDeclarator <: Node { type: "VariableDeclarator"; id: Pattern; init: Expression | null; }
標識符,我以爲應該是這麼叫的,就是咱們寫 JS 時自定義的名稱,如變量名,函數名,屬性名,都歸爲標識符。相應的接口是這樣的:
interface Identifier <: Expression, Pattern { type: "Identifier"; name: string; }
一個標識符多是一個表達式,或者是解構的模式(ES6 中的解構語法)。咱們等會會看到 Expression
和 Pattern
相關的內容的。
字面量,這裏不是指 []
或者 {}
這些,而是自己語義就表明了一個值的字面量,如 1
,「hello」
, true
這些,還有正則表達式(有一個擴展的 Node
來表示正則表達式),如 /\d?/
。咱們看一下文檔的定義:
interface Literal <: Expression { type: "Literal"; value: string | boolean | null | number | RegExp; }
value
這裏即對應了字面量的值,咱們能夠看出字面量值的類型,字符串,布爾,數值,null
和正則。
let a = 3+4
二元運算表達式節點,left
和 right
表示運算符左右的兩個表達式,operator
表示一個二元運算符。
interface BinaryExpression <: Expression { type: "BinaryExpression"; operator: BinaryOperator; left: Expression; right: Expression; }
二元運算符,全部值以下:
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in" | "instanceof" }
if(a === 0){ }
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; }
常見的 AST node types 在 Babylon 中 定義以下:
符合規範的解析出來的 AST 節點用 Node
對象來標識,Node
對象應該符合這樣的接口:
interface Node { type: string; loc: SourceLocation | null; }
type
字段表示不一樣的節點類型,下邊會再講一下各個類型的狀況,分別對應了 JavaScript 中的什麼語法。loc
字段表示源碼的位置信息,若是沒有相關信息的話爲 null
,不然是一個對象,包含了開始和結束的位置。接口以下:
interface SourceLocation { source: string | null; start: Position; end: Position; }
這裏的 Position
對象包含了行和列的信息,行從 1 開始,列從 0 開始:
interface Position { line: number; // >= 1 column: number; // >= 0 }
標識符,就是咱們寫 JS 時自定義的名稱,如變量名,函數名,屬性名,都歸爲標識符。相應的接口是這樣的:
interface Identifier <: Expression, Pattern { type: "Identifier"; name: string; }
一個標識符多是一個表達式,或者是解構的模式(ES6 中的解構語法)。咱們等會會看到 Expression
和 Pattern
相關的內容的。
interface PrivateName <: Expression, Pattern { type: "PrivateName"; id: Identifier; }
A Private Name Identifier.
字面量,這裏不是指 []
或者 {}
這些,而是自己語義就表明了一個值的字面量,如 1
,「hello」
, true
這些,還有正則表達式(有一個擴展的 Node
來表示正則表達式),如 /\d?/
。咱們看一下文檔的定義:
interface Literal <: Expression { type: "Literal"; value: string | boolean | null | number | RegExp; }
value
這裏即對應了字面量的值,咱們能夠看出字面量值的類型,字符串,布爾,數值,null
和正則。
這個針對正則字面量的,爲了更好地來解析正則表達式的內容,添加多一個 regex
字段,裏邊會包括正則自己,以及正則的 flags
。
interface RegExpLiteral <: Literal { regex: { pattern: string; flags: string; }; }
通常這個是做爲根節點的,即表明了一棵完整的程序代碼樹。
interface Program <: Node { type: "Program"; body: [ Statement ]; }
body
屬性是一個數組,包含了多個 Statement
(即語句)節點。
函數聲明或者函數表達式節點。
interface Function <: Node { id: Identifier | null; params: [ Pattern ]; body: BlockStatement; }
id
是函數名,params
屬性是一個數組,表示函數的參數。body
是一個塊語句。
有一個值得留意的點是,你在測試過程當中,是不會找到 type: "Function"
的節點的,可是你能夠找到 type: "FunctionDeclaration"
和 type: "FunctionExpression"
,由於函數要麼以聲明語句出現,要麼以函數表達式出現,都是節點類型的組合類型,後邊會再說起 FunctionDeclaration
和 FunctionExpression
的相關內容。
語句節點沒什麼特別的,它只是一個節點,一種區分,可是語句有不少種,下邊會詳述。
interface Statement <: Node { }
表達式語句節點,a = a + 1
或者 a++
裏邊會有一個 expression
屬性指向一個表達式節點對象(後邊會說起表達式)。
interface ExpressionStatement <: Statement { type: "ExpressionStatement"; expression: Expression; }
塊語句節點,舉個例子:if (...) { // 這裏是塊語句的內容 }
,塊裏邊能夠包含多個其餘的語句,因此有一個 body
屬性,是一個數組,表示了塊裏邊的多個語句。
interface BlockStatement <: Statement { type: "BlockStatement"; body: [ Statement ]; }
返回語句節點,argument
屬性是一個表達式,表明返回的內容。
interface ReturnStatement <: Statement { type: "ReturnStatement"; argument: Expression | null; }
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
語句節點,有兩個屬性,discriminant
屬性表示 switch
語句後緊隨的表達式,一般會是一個變量,cases
屬性是一個 case
節點的數組,用來表示各個 case
語句。
interface SwitchStatement <: Statement { type: "SwitchStatement"; discriminant: Expression; cases: [ SwitchCase ]; }
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; }
聲明語句節點,一樣也是語句,只是一個類型的細化。下邊會介紹各類聲明語句類型。
interface Declaration <: Statement { }
函數聲明,和以前提到的 Function 不一樣的是,id
不能爲 null
。
interface FunctionDeclaration <: Function, Declaration { type: "FunctionDeclaration"; id: Identifier; }
變量聲明,kind
屬性表示是什麼類型的聲明,由於 ES6 引入了 const/let
。declarations
表示聲明的多個描述,由於咱們能夠這樣:let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var"; }
變量聲明的描述,id
表示變量名稱節點,init
表示初始值的表達式,能夠爲 null
。
interface VariableDeclarator <: Node { type: "VariableDeclarator"; id: Pattern; init: Expression | null; }
表達式節點。
interface Expression <: Node { }
interface Import <: Node { type: "Import"; }
數組表達式節點,elements
屬性是一個數組,表示數組的多個元素,每個元素都是一個表達式節點。
interface ArrayExpression <: Expression { type: "ArrayExpression"; elements: [ Expression | null ]; }
對象表達式節點,property
屬性是一個數組,表示對象的每個鍵值對,每個元素都是一個屬性節點。
interface ObjectExpression <: Expression { type: "ObjectExpression"; properties: [ Property ]; }
對象表達式中的屬性節點。key
表示鍵,value
表示值,因爲 ES5 語法中有 get/set
的存在,因此有一個 kind
屬性,用來表示是普通的初始化,或者是 get/set
。
interface Property <: Node { type: "Property"; key: Literal | Identifier; value: Expression; kind: "init" | "get" | "set"; }
函數表達式節點。
interface FunctionExpression <: Function, Expression { type: "FunctionExpression"; }
二元運算表達式節點,left
和 right
表示運算符左右的兩個表達式,operator
表示一個二元運算符。
interface BinaryExpression <: Expression { type: "BinaryExpression"; operator: BinaryOperator; left: Expression; right: Expression; }
二元運算符,全部值以下:
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in" | "instanceof" }
賦值表達式節點,operator
屬性表示一個賦值運算符,left
和 right
是賦值運算符左右的表達式。
interface AssignmentExpression <: Expression { type: "AssignmentExpression"; operator: AssignmentOperator; left: Pattern | Expression; right: Expression; }
賦值運算符,全部值以下:(經常使用的並很少)
enum AssignmentOperator { "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "<<=" | ">>=" | ">>>=" | "|=" | "^=" | "&=" }
條件表達式,一般咱們稱之爲三元運算表達式,即 boolean ? true : false
。屬性參考條件語句。
interface ConditionalExpression <: Expression { type: "ConditionalExpression"; test: Expression; alternate: Expression; consequent: Expression; }
interface Decorator <: Node { type: "Decorator"; expression: Expression; }
模式,主要在 ES6 的解構賦值中有意義,在 ES5 中,能夠理解爲和 Identifier
差很少的東西。
interface Pattern <: Node { }
interface Class <: Node { id: Identifier | null; superClass: Expression | null; body: ClassBody; decorators: [ Decorator ]; }
interface ClassBody <: Node { type: "ClassBody"; body: [ ClassMethod | ClassPrivateMethod | ClassProperty | ClassPrivateProperty ]; }
interface ClassMethod <: Function { type: "ClassMethod"; key: Expression; kind: "constructor" | "method" | "get" | "set"; computed: boolean; static: boolean; decorators: [ Decorator ]; }
interface ImportDeclaration <: ModuleDeclaration { type: "ImportDeclaration"; specifiers: [ ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier ]; source: Literal; }
import 聲明,如: import foo from "mod";
想知道完整的核心 Babylon AST node types,可查看 babylon spec.md。這裏僅僅只是列出目錄,方便你們對造成清晰完整的概念。
剛開始原本是準備講解 Babel 及經常使用模塊的,後來發現內容太龐大,一篇文章根本容納不了,因而改成只關注 Babel 的代碼解析 Babylon 部分,結果隨便一整理,又是這麼長,唉。。。只能這樣子了。