精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

使用Acorn來解析JavaScript

開發 前端
因為最近工作上有需要使用解析 JavaScript 的代碼,大部分情況使用正則表達式匹配就可以處理,但是一旦依賴于代碼上下文的內容時,正則或者簡單的字符解析就很力不從心了,這個時候需要一個語言解析器來獲取整一個 AST(abstract syntax tree)。

[[176219]]

Talk

因為最近工作上有需要使用解析 JavaScript 的代碼,大部分情況使用正則表達式匹配就可以處理,但是一旦依賴于代碼上下文的內容時,正則或者簡單的字符解析就很力不從心了,這個時候需要一個語言解析器來獲取整一個 AST(abstract syntax tree)。

然后我找到了多個使用 JavaScript 編寫的 JavaScript 解析器:

  • Esprima
  • Acorn
  • UglifyJS 2
  • Shift

從提交記錄來看,維護情況都蠻好的,ES 各種發展的特性都跟得上,我分別都簡單了解了一下,聊聊他們的一些情況。

<!--more-->

Esprima 是很經典的一個解析器,Acorn 在它之后誕生,都是幾年前的事情了。按照 Acorn 作者的說法,當時造這個輪子更多只是好玩,速度可以和 Esprima 媲美,但是實現代碼更少。其中比較關鍵的點是這兩個解析器出來的 AST 結果(對,只是 AST,tokens 不一樣)都是符合 The Estree Spec 規范(這是 Mozilla 的工程師給出的 SpiderMonkey 引擎輸出的 JavaScript AST 的規范文檔,也可以參考:SpiderMonkey in MDN)的,也就是得到的結果在很大部分上是兼容的。

現在很出名的 Webpack 解析代碼時用的也是 Acorn。

至于 Uglify,很出名的一個 JavaScript 代碼壓縮器,其實它自帶了一個代碼解析器,也可以輸出 AST,但是它的功能更多還是用于壓縮代碼,如果拿來解析代碼感覺不夠純粹。

Shift 這個沒做多少了解,只知道他定義了自己的一套 AST 規范。

Esprima 官網上有一個性能測試,我在 chrome 上跑的結果如下:

<img src="http://ww1.sinaimg.cn/large/0... alt="性能測試" style="width:100%;">

可見,Acorn 的性能很不錯,而且還有一個 Estree 的規范呢(規范很重要,我個人覺得遵循通用的規范是代碼復用的重要基礎),所以我就直接選用 Acorn 來做代碼解析了。

圖中做性能對比的還有 Google 的 Traceur,它更多是一個 ES6 to ES5 的 compiler,于我們想要找的解析器定位不符。

下面進入正題,如何使用 Acorn 來解析 JavaScript。

API

解析器的 API 都是很簡單的:

  1. const ast = acorn.parse(code, options) 

Acorn 的配置項蠻多的,里邊還包括了一些事件可以設置回調函數。我們挑幾個比較重要的講下:

  • ecmaVersion

字面意義,很好理解,就是設置你要解析的 JavaScript 的 ECMA 版本。默認是 ES7。

  • sourceType

這個配置項有兩個值:module 和 script,默認是 script。

主要是嚴格模式和 import/export 的區別。ES6 中的模塊是嚴格模式,也就是你無須添加 use strict。我們通常瀏覽器中使用的 script 是沒有 import/export 語法的。

所以,選擇了 script 則出現 import/export 會報錯,可以使用嚴格模式聲明,選擇了 module,則不用嚴格模式聲明,可以使用import/export 語法。

  • locations

默認值是 false,設置為 true 之后會在 AST 的節點中攜帶多一個 loc 對象來表示當前的開始和結束的行數和列數。

  • onComment

傳入一個回調函數,每當解析到代碼中的注釋時會觸發,可以獲取當年注釋內容,參數列表是:[block, text, start, end]。

block 表示是否是塊注釋,text 是注釋內容,start 和 end 是注釋開始和結束的位置。

上邊提及的 Espree 需要 Esprima 的 attachComment 的配置項,設置為 true 后,Esprima 會在代碼解析結果的節點中攜帶注釋相關信息(trailingComments 和 leadingComments)。Espree 則是利用 Acorn 的 onComment 配置來實現這個 Esprima 特性的兼容。

解析器通常還會有一個獲取詞法分析結果的接口:

  1. const tokens = [...acorn.tokenizer(code, options)] 

tokenizer 方法的第二個參數也能夠配置 locations。

詞法結果 token 和 Esprima 的結果數據結構上有一定的區別(Espree 又是做了這一層的兼容),有興趣了解的可以看下 Esprima 的解析結果:http://esprima.org/demo/parse... 。

至于 Acorn 解析的 AST 和 token 的內容我們接下來詳述。

Token

我找了半天,沒找到關于 token 數據結構的詳細介紹,只能自己動手來看一下了。

我用來測試解析的代碼是:

  1. import "hello.js" 
  2.  
  3. var a = 2; 
  4.  
  5. // test 
  6. function name() { console.log(arguments); }  

解析出來的 token 數組是一個個類似這樣的對象:

  1. Token { 
  2.     type: 
  3.      TokenType { 
  4.        label: 'import'
  5.        keyword: 'import'
  6.        beforeExpr: false
  7.        startsExpr: false
  8.        isLoop: false
  9.        isAssign: false
  10.        prefix: false
  11.        postfix: false
  12.        binop: null
  13.        updateContext: null }, 
  14.     value: 'import'
  15.     start: 5, 
  16.     end: 11 },  

看上去其實很好理解對不對,在 type 對應的對象中,label 表示當前標識的一個類型,keyword 就是關鍵詞,像例子中的import,或者 function 之類的。

value 則是當前標識的值,start/end 分別是開始和結束的位置。

通常我們需要關注的就是 label/keyword/value 這些了。其他的詳細可以參考源碼:tokentype.js。

The Estree Spec

這一部分是重頭戲,因為實際上我需要的還是解析出來的 AST。最原滋原味的內容來自于:The Estree Spec,我只是閱讀了之后的搬運工。

提供了標準文檔的好處是,很多東西有跡可循,這里還有一個工具,用于把滿足 Estree 標準的 AST 轉換為 ESMAScript 代碼:escodegen。

好吧,回到正題,我們先來看一下 ES5 的部分,可以在 Esprima: Parser 這個頁面測試各種代碼的解析結果。

符合這個規范的解析出來的 AST 節點用 Node 對象來標識,Node 對象應該符合這樣的接口:

  1. interface Node { 
  2.     type: string; 
  3.     loc: SourceLocation | null
  4.  

type 字段表示不同的節點類型,下邊會再講一下各個類型的情況,分別對應了 JavaScript 中的什么語法。

loc 字段表示源碼的位置信息,如果沒有相關信息的話為 null,否則是一個對象,包含了開始和結束的位置。接口如下:

  1. interface SourceLocation { 
  2.     source: string | null
  3.     start: Position; 
  4.     end: Position; 
  5.  

這里的 Position 對象包含了行和列的信息,行從 1 開始,列從 0 開始:

  1. interface Position { 
  2.     line: number; // >= 1 
  3.     column: number; // >= 0 
  4.  

好了,基礎部分就是這樣,接下來看各種類型的節點,順帶溫習一下 JavaScript 語法的一些東西吧。對于這里每一部分的內容,會簡單談一下,但不會展開(內容不少),對 JavaScript 了解的人很容易就明白的。

我覺得看完就像把 JavaScript 的基礎語法整理了一遍。

Identifier

標識符,我覺得應該是這么叫的,就是我們寫 JS 時自定義的名稱,如變量名,函數名,屬性名,都歸為標識符。相應的接口是這樣的:

  1. interface Identifier <: Expression, Pattern { 
  2.     type: "Identifier"
  3.     name: string; 
  4.  

一個標識符可能是一個表達式,或者是解構的模式(ES6 中的解構語法)。我們等會會看到 Expression 和 Pattern 相關的內容的。

Literal

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

  1. interface Literal <: Expression { 
  2.     type: "Literal"
  3.     value: string | boolean | null | number | RegExp; 
  4.  

value 這里即對應了字面量的值,我們可以看出字面量值的類型,字符串,布爾,數值,null 和正則。

RegExpLiteral

這個針對正則字面量的,為了更好地來解析正則表達式的內容,添加多一個 regex 字段,里邊會包括正則本身,以及正則的flags。

  1. interface RegExpLiteral <: Literal { 
  2.   regex: { 
  3.     pattern: string; 
  4.     flags: string; 
  5.   }; 
  6.  

Programs

一般這個是作為跟節點的,即代表了一棵完整的程序代碼樹。

  1. interface Program <: Node { 
  2.     type: "Program"
  3.     body: [ Statement ]; 
  4.  

body 屬性是一個數組,包含了多個 Statement(即語句)節點。

Functions

函數聲明或者函數表達式節點。

  1. interface Function <: Node { 
  2.     id: Identifier | null
  3.     params: [ Pattern ]; 
  4.     body: BlockStatement; 
  5.  

id 是函數名,params 屬性是一個數組,表示函數的參數。body 是一個塊語句。

有一個值得留意的點是,你在測試過程中,是不會找到 type: "Function" 的節點的,但是你可以找到 type: "FunctionDeclaration" 和 type: "FunctionExpression",因為函數要么以聲明語句出現,要么以函數表達式出現,都是節點類型的組合類型,后邊會再提及 FunctionDeclaration 和 FunctionExpression 的相關內容。

這讓人感覺這個文檔規劃得蠻細致的,函數名,參數和函數塊是屬于函數部分的內容,而聲明或者表達式則有它自己需要的東西。

Statement

語句節點沒什么特別的,它只是一個節點,一種區分,但是語句有很多種,下邊會詳述。

  1. interface Statement <: Node { } 

ExpressionStatement

表達式語句節點,a = a + 1 或者 a++ 里邊會有一個 expression 屬性指向一個表達式節點對象(后邊會提及表達式)。

  1. interface ExpressionStatement <: Statement { 
  2.     type: "ExpressionStatement"
  3.     expression: Expression; 
  4.  

BlockStatement

塊語句節點,舉個例子:if (...) { // 這里是塊語句的內容 },塊里邊可以包含多個其他的語句,所以有一個 body 屬性,是一個數組,表示了塊里邊的多個語句。

  1. interface BlockStatement <: Statement { 
  2.     type: "BlockStatement"
  3.     body: [ Statement ]; 
  4.  

EmptyStatement

一個空的語句節點,沒有執行任何有用的代碼,例如一個單獨的分號 ;

  1. interface EmptyStatement  <: Statement { 
  2.     type: "EmptyStatement "
  3.  

DebuggerStatement

debugger,就是表示這個,沒有其他了。

  1. interface DebuggerStatement <: Statement { 
  2.     type: "DebuggerStatement"
  3.  

WithStatement

with 語句節點,里邊有兩個特別的屬性,object 表示 with 要使用的那個對象(可以是一個表達式),body 則是對應 with 后邊要執行的語句,一般會是一個塊語句。

  1. interface WithStatement <: Statement { 
  2.     type: "WithStatement"
  3.     object: Expression; 
  4.     body: Statement; 
  5.  

下邊是控制流的語句:

ReturnStatement

返回語句節點,argument 屬性是一個表達式,代表返回的內容。 

  1. interface ReturnStatement <: Statement { 
  2.     type: "ReturnStatement"
  3.     argument: Expression | null
  4.  

LabeledStatement

label 語句,平時可能會比較少接觸到,舉個例子:

  1. loop: for(let i = 0; i < len; i++) { 
  2.     // ... 
  3.     for (let j = 0; j < min; j++) { 
  4.         // ... 
  5.         break loop; 
  6.     } 
  7.  

這里的 loop 就是一個 label 了,我們可以在循環嵌套中使用 break loop 來指定跳出哪個循環。所以這里的 label 語句指的就是loop: ... 這個。

一個 label 語句節點會有兩個屬性,一個 label 屬性表示 label 的名稱,另外一個 body 屬性指向對應的語句,通常是一個循環語句或者 switch 語句。

  1. interface LabeledStatement <: Statement { 
  2.     type: "LabeledStatement"
  3.     label: Identifier; 
  4.     body: Statement; 
  5.  

BreakStatement

break 語句節點,會有一個 label 屬性表示需要的 label 名稱,當不需要 label 的時候(通常都不需要),便是 null。

  1. interface BreakStatement <: Statement { 
  2.     type: "BreakStatement"
  3.     label: Identifier | null
  4.  

ContinueStatement

continue 語句節點,和 break 類似。

  1. interface ContinueStatement <: Statement { 
  2.     type: "ContinueStatement"
  3.     label: Identifier | null
  4.  

下邊是條件語句:

IfStatement

if 語句節點,很常見,會帶有三個屬性,test 屬性表示 if (...) 括號中的表達式。

consequent 屬性是表示條件為 true 時的執行語句,通常會是一個塊語句。

alternate 屬性則是用來表示 else 后跟隨的語句節點,通常也會是塊語句,但也可以又是一個 if 語句節點,即類似這樣的結構:

if (a) { //... } else if (b) { // ... }。

alternate 當然也可以為 null。

  1. interface IfStatement <: Statement { 
  2.     type: "IfStatement"
  3.     test: Expression; 
  4.     consequent: Statement; 
  5.     alternate: Statement | null
  6.  

SwitchStatement

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

  1. interface SwitchStatement <: Statement { 
  2.     type: "SwitchStatement"
  3.     discriminant: Expression; 
  4.     cases: [ SwitchCase ]; 
  5.  

SwitchCase

switch 的 case 節點。test 屬性代表這個 case 的判斷表達式,consequent 則是這個 case 的執行語句。

當 test 屬性是 null 時,則是表示 default 這個 case 節點。 

  1. interface SwitchCase <: Node { 
  2.     type: "SwitchCase"
  3.     test: Expression | null
  4.     consequent: [ Statement ]; 
  5.  

下邊是異常相關的語句:

ThrowStatement

throw 語句節點,argument 屬性用以表示 throw 后邊緊跟的表達式。

  1. interface ThrowStatement <: Statement { 
  2.     type: "ThrowStatement"
  3.     argument: Expression; 
  4.  

TryStatement

try 語句節點,block 屬性表示 try 的執行語句,通常是一個塊語句。

hanlder 屬性是指 catch 節點,finalizer 是指 finally 語句節點,當 hanlder 為 null 時,finalizer 必須是一個塊語句節點。

  1. interface TryStatement <: Statement { 
  2.     type: "TryStatement"
  3.     block: BlockStatement; 
  4.     handler: CatchClause | null
  5.     finalizer: BlockStatement | null
  6.  

CatchClause

catch 節點,param 用以表示 catch 后的參數,body 則表示 catch 后的執行語句,通常是一個塊語句。

  1. interface CatchClause <: Node { 
  2.     type: "CatchClause"
  3.     param: Pattern; 
  4.     body: BlockStatement; 
  5.  

下邊是循環語句:

WhileStatement

while 語句節點,test 表示括號中的表達式,body 是表示要循環執行的語句。

  1. interface WhileStatement <: Statement { 
  2.     type: "WhileStatement"
  3.     test: Expression; 
  4.     body: Statement; 
  5.  

DoWhileStatement

do/while 語句節點,和 while 語句類似。

  1. interface DoWhileStatement <: Statement { 
  2.     type: "DoWhileStatement"
  3.     body: Statement; 
  4.     test: Expression; 
  5.  

ForStatement

for 循環語句節點,屬性 init/test/update 分別表示了 for 語句括號中的三個表達式,初始化值,循環判斷條件,每次循環執行的變量更新語句(init 可以是變量聲明或者表達式)。這三個屬性都可以為 null,即 for(;;){}。

body 屬性用以表示要循環執行的語句。

  1. interface ForStatement <: Statement { 
  2.     type: "ForStatement"
  3.     init: VariableDeclaration | Expression | null
  4.     test: Expression | null
  5.     update: Expression | null
  6.     body: Statement; 
  7.  

ForInStatement

for/in 語句節點,left 和 right 屬性分別表示在 in 關鍵詞左右的語句(左側可以是一個變量聲明或者表達式)。body 依舊是表示要循環執行的語句。

  1. interface ForInStatement <: Statement { 
  2.     type: "ForInStatement"
  3.     left: VariableDeclaration |  Pattern; 
  4.     right: Expression; 
  5.     body: Statement; 
  6.  

Declarations

聲明語句節點,同樣也是語句,只是一個類型的細化。下邊會介紹各種聲明語句類型。

  1. interface Declaration <: Statement { } 

FunctionDeclaration

函數聲明,和之前提到的 Function 不同的是,id 不能為 null。

  1. interface FunctionDeclaration <: Function, Declaration { 
  2.     type: "FunctionDeclaration"
  3.     id: Identifier; 
  4.  

VariableDeclaration

變量聲明,kind 屬性表示是什么類型的聲明,因為 ES6 引入了 const/let。

declarations 表示聲明的多個描述,因為我們可以這樣:let a = 1, b = 2;。

  1. interface VariableDeclaration <: Declaration { 
  2.     type: "VariableDeclaration"
  3.     declarations: [ VariableDeclarator ]; 
  4.     kind: "var"
  5.  

VariableDeclarator

變量聲明的描述,id 表示變量名稱節點,init 表示初始值的表達式,可以為 null。

  1. interface VariableDeclarator <: Node { 
  2.     type: "VariableDeclarator"
  3.     id: Pattern; 
  4.     init: Expression | null
  5.  

Expressions

表達式節點。

  1. interface Expression <: Node { } 

ThisExpression

表示 this。

  1. interface ThisExpression <: Expression { 
  2.     type: "ThisExpression"
  3.  

ArrayExpression

數組表達式節點,elements 屬性是一個數組,表示數組的多個元素,每一個元素都是一個表達式節點。

  1. interface ArrayExpression <: Expression { 
  2.     type: "ArrayExpression"
  3.     elements: [ Expression | null ]; 
  4.  

ObjectExpression

對象表達式節點,property 屬性是一個數組,表示對象的每一個鍵值對,每一個元素都是一個屬性節點。

  1. interface ObjectExpression <: Expression { 
  2.     type: "ObjectExpression"
  3.     properties: [ Property ]; 
  4.  

Property

對象表達式中的屬性節點。key 表示鍵,value 表示值,由于 ES5 語法中有 get/set 的存在,所以有一個 kind 屬性,用來表示是普通的初始化,或者是 get/set。

  1. interface Property <: Node { 
  2.     type: "Property"
  3.     key: Literal | Identifier; 
  4.     value: Expression; 
  5.     kind: "init" | "get" | "set"
  6.  

FunctionExpression

函數表達式節點。

  1. interface FunctionExpression <: Function, Expression { 
  2.     type: "FunctionExpression"
  3.  

下邊是一元運算符相關的表達式部分:

UnaryExpression

一元運算表達式節點(++/-- 是 update 運算符,不在這個范疇內),operator 表示運算符,prefix 表示是否為前綴運算符。argument 是要執行運算的表達式。

  1. interface UnaryExpression <: Expression { 
  2.     type: "UnaryExpression"
  3.     operator: UnaryOperator; 
  4.     prefix: boolean; 
  5.     argument: Expression; 
  6.  

UnaryOperator

一元運算符,枚舉類型,所有值如下:

  1. enum UnaryOperator { 
  2.     "-" | "+" | "!" | "~" | "typeof" | "void" | "delete" 
  3.  

UpdateExpression

update 運算表達式節點,即 ++/--,和一元運算符類似,只是 operator 指向的節點對象類型不同,這里是 update 運算符。

  1. interface UpdateExpression <: Expression { 
  2.     type: "UpdateExpression"
  3.     operator: UpdateOperator; 
  4.     argument: Expression; 
  5.     prefix: boolean; 
  6.  

UpdateOperator

update 運算符,值為 ++ 或 --,配合 update 表達式節點的 prefix 屬性來表示前后。

  1. enum UpdateOperator { 
  2.   "++" | "--" 
  3.  

下邊是二元運算符相關的表達式部分:

BinaryExpression

二元運算表達式節點,left 和 right 表示運算符左右的兩個表達式,operator 表示一個二元運算符。

  1. interface BinaryExpression <: Expression { 
  2.     type: "BinaryExpression"
  3.     operator: BinaryOperator; 
  4.     left: Expression; 
  5.     right: Expression; 
  6. } 

BinaryOperator

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

  1. enum BinaryOperator { 
  2.     "==" | "!=" | "===" | "!==" 
  3.          | "<" | "<=" | ">" | ">=" 
  4.          | "<<" | ">>" | ">>>" 
  5.          | "+" | "-" | "*" | "/" | "%" 
  6.          | "|" | "^" | "&" | "in" 
  7.          | "instanceof" 
  8.  

AssignmentExpression

賦值表達式節點,operator 屬性表示一個賦值運算符,left 和 right 是賦值運算符左右的表達式。

  1. interface AssignmentExpression <: Expression { 
  2.     type: "AssignmentExpression"
  3.     operator: AssignmentOperator; 
  4.     left: Pattern | Expression; 
  5.     right: Expression; 
  6.  

AssignmentOperator

賦值運算符,所有值如下:(常用的并不多)

  1. enum AssignmentOperator { 
  2.     "=" | "+=" | "-=" | "*=" | "/=" | "%=" 
  3.         | "<<=" | ">>=" | ">>>=" 
  4.         | "|=" | "^=" | "&=" 
  5.  

LogicalExpression

邏輯運算表達式節點,和賦值或者二元運算類型,只不過 operator 是邏輯運算符類型。

  1. interface LogicalExpression <: Expression { 
  2.     type: "LogicalExpression"
  3.     operator: LogicalOperator; 
  4.     left: Expression; 
  5.     right: Expression; 
  6.  

LogicalOperator

邏輯運算符,兩種值,即與或。

  1. enum LogicalOperator { 
  2.     "||" | "&&" 
  3.  

MemberExpression

成員表達式節點,即表示引用對象成員的語句,object 是引用對象的表達式節點,property 是表示屬性名稱,computed 如果為false,是表示 . 來引用成員,property 應該為一個 Identifier 節點,如果 computed 屬性為 true,則是 [] 來進行引用,即property 是一個 Expression 節點,名稱是表達式的結果值。

  1. interface MemberExpression <: Expression, Pattern { 
  2.     type: "MemberExpression"
  3.     object: Expression; 
  4.     property: Expression; 
  5.     computed: boolean; 
  6.  

下邊是其他的一些表達式:

ConditionalExpression

條件表達式,通常我們稱之為三元運算表達式,即 boolean ? true : false。屬性參考條件語句。

  1. interface ConditionalExpression <: Expression { 
  2.     type: "ConditionalExpression"
  3.     test: Expression; 
  4.     alternate: Expression; 
  5.     consequent: Expression; 
  6.  

CallExpression

函數調用表達式,即表示了 func(1, 2) 這一類型的語句。callee 屬性是一個表達式節點,表示函數,arguments 是一個數組,元素是表達式節點,表示函數參數列表。

  1. interface CallExpression <: Expression { 
  2.     type: "CallExpression"
  3.     callee: Expression; 
  4.     arguments: [ Expression ]; 
  5.  

NewExpression

new 表達式。

  1. interface NewExpression <: CallExpression { 
  2.     type: "NewExpression"
  3.  

SequenceExpression

這個就是逗號運算符構建的表達式(不知道確切的名稱),expressions 屬性為一個數組,即表示構成整個表達式,被逗號分割的多個表達式。

  1. interface SequenceExpression <: Expression { 
  2.     type: "SequenceExpression"
  3.     expressions: [ Expression ]; 
  4.  

Patterns

模式,主要在 ES6 的解構賦值中有意義,在 ES5 中,可以理解為和 Identifier 差不多的東西。

  1. interface Pattern <: Node { } 

這一部分的內容比較多,但都可以舉一反三,寫這個的時候我就當把 JavaScript 語法再復習一遍。這個文檔還有 ES2015,ES2016,ES2017 相關的內容,涉及的東西也蠻多,但是理解了上邊的這一些,然后從語法層面去思考這個文檔,其他的內容也就很好理解了,這里略去,有需要請參閱:The Estree Spec。

Plugins

回到我們的主角,Acorn,提供了一種擴展的方式來編寫相關的插件:Acorn Plugins。

我們可以使用插件來擴展解析器,來解析更多的一些語法,如 .jsx 語法,有興趣的看看這個插件:acorn-jsx。

官方表示 Acorn 的插件是用于方便擴展解析器,但是需要對 Acorn 內部的運行***比較了解,擴展的方式會在原本的基礎上重新定義一些方法。這里不展開講了,如果我需要插件的話,會再寫文章聊聊這個東西。

Examples

現在我們來看一下如何應用這個解析器,例如我們需要用來解析出一個符合 CommonJS 規范的模塊依賴了哪些模塊,我們可以用 Acorn 來解析 require 這個函數的調用,然后取出調用時的傳入參數,便可以獲取依賴的模塊。

下邊是示例代碼:

  1. // 遍歷所有節點的函數 
  2. function walkNode(node, callback) { 
  3.   callback(node) 
  4.  
  5.   // 有 type 字段的我們認為是一個節點 
  6.   Object.keys(node).forEach((key) => { 
  7.     const item = node[key
  8.     if (Array.isArray(item)) { 
  9.       item.forEach((sub) => { 
  10.         sub.type && walkNode(sub, callback) 
  11.       }) 
  12.     } 
  13.  
  14.     item && item.type && walkNode(item, callback) 
  15.   }) 
  16.  
  17. function parseDependencies(str) { 
  18.   const ast = acorn.parse(str, { ranges: true }) 
  19.   const resource = [] // 依賴列表 
  20.  
  21.   // 從根節點開始 
  22.   walkNode(ast, (node) => { 
  23.     const callee = node.callee 
  24.     const args = node.arguments 
  25.  
  26.     // require 我們認為是一個函數調用,并且函數名為 require,參數只有一個,且必須是字面量 
  27.     if ( 
  28.       node.type === 'CallExpression' && 
  29.       callee.type === 'Identifier' && 
  30.       callee.name === 'require' && 
  31.       args.length === 1 && 
  32.       args[0].type === 'Literal' 
  33.     ) { 
  34.       const args = node.arguments 
  35.  
  36.       // 獲取依賴的相關信息 
  37.       resource.push({ 
  38.         string: str.substring(node.range[0], node.range[1]), 
  39.         path: args[0].value, 
  40.         start: node.range[0], 
  41.         end: node.range[1] 
  42.       }) 
  43.     } 
  44.   }) 
  45.  
  46.   return resource 
  47.  

這只是簡單的一個情況的處理,但是已經給我們呈現了如何使用解析器,Webpack 則在這個的基礎上做了更多的東西,包括 var r = require; r('a') 或者 require.async('a') 等的處理。

AST 這個東西對于前端來說,我們無時無刻不在享受著它帶來的成果(模塊構建,代碼壓縮,代碼混淆),所以了解一下總歸有好處。

有問題歡迎討論。

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2019-02-26 13:00:11

JavaScriptURL前端

2021-08-11 22:50:53

JavaScript編程開發

2010-10-08 12:46:27

Javascriptreplace()

2010-10-08 14:27:25

JavascriptSplit

2022-04-07 08:00:00

Javascript開發

2013-04-08 12:41:35

JavaScriptJS

2021-07-29 10:00:43

XMLXMLStarletLinux

2023-06-06 07:50:50

Symbol類型ECMAScript

2019-12-11 09:23:51

JavaScriptHTMLXML

2011-03-15 09:50:41

XMLWeb

2009-03-23 13:08:07

PHP擴展PHPJavascript

2019-12-06 10:59:20

JavaScript運行引擎

2017-05-23 15:47:04

JavaScriptthis解析

2011-09-09 19:23:52

Widget

2024-03-28 10:17:03

JDK 17字符串十六進制

2013-02-18 11:31:00

JavaScriptPerl語言

2010-09-26 17:21:07

2016-10-09 08:38:01

JavaScript瀏覽器事件

2010-10-08 15:11:28

JavaScript數

2020-06-01 08:39:12

JavaScript開發技術
點贊
收藏

51CTO技術棧公眾號

国产精品伦一区二区| 国产精品一区无码| 日韩第一区第二区| 亚洲成a人v欧美综合天堂| 明星裸体视频一区二区| 在线观看中文字幕2021| 欧美日韩网站| 国产一区二区三区在线观看视频| aaa一级黄色片| 波多野结衣精品| 久久久亚洲午夜电影| 国产精品爽爽爽| 久久精品亚洲无码| 成人免费在线播放| 亚洲精美色品网站| 精品综合久久久久| 天堂√中文最新版在线| 亚洲日韩欧美一区二区在线| 国产视频精品网| 91国产免费视频| 亚洲综合激情| 欧美高跟鞋交xxxxxhd| 国产成人一区二区在线观看| h视频久久久| 欧美精品丝袜久久久中文字幕| 欧美在线中文字幕| 91精品在线国产| 国产一二三四在线| 五月久久久综合一区二区小说| 日韩精品小视频| 丰满少妇一区二区三区专区| 日本欧美韩国| 欧美日韩中文字幕日韩欧美| 免费一级淫片aaa片毛片a级| 麻豆网站在线| 中文字幕免费在线观看视频一区| 色琪琪一区二区三区亚洲区| 日本一区二区三区免费观看| 日本波多野结衣在线| 久久97超碰色| 国产精品吴梦梦| 久久久久久久久久成人| 国产欧美日韩一级| 久久久亚洲影院| 欧美精品入口蜜桃| 欧美成人久久| 国产一区二区主播在线| 久久久亚洲高清| 好吊色欧美一区二区三区| www.av在线.com| 国产一区二区三区久久久| 国产日韩av在线| 中文资源在线播放| 蜜臀av亚洲一区中文字幕| 国产suv精品一区二区| 九九精品免费视频| 裸体一区二区| 日韩女优在线播放| 亚洲精品91天天久久人人| 久久精品中文| 国产精品678| 中国女人真人一级毛片| 奇米色777欧美一区二区| 国产成人极品视频| 久久这里只有精品9| 蜜臂av日日欢夜夜爽一区| 国产精品自产拍在线观看| 中文字幕一区二区三区四区免费看 | 午夜在线小视频| 亚洲深夜福利| 55夜色66夜色国产精品视频| 免费一级a毛片夜夜看| 国产综合激情| 午夜精品福利视频| 97久久久久久久| 日韩精品一卡二卡三卡四卡无卡| 国产精品日韩在线播放| 国产精品视频一二区| 国产高清不卡二三区| 国产精品日韩一区二区三区 | 亚洲国产中文字幕久久网| 波多野结衣 在线| 日韩精品诱惑一区?区三区| 久久精品中文字幕| 国产香蕉视频在线| 奇米精品一区二区三区在线观看一| 成人午夜一级二级三级| 亚洲精品一区二区三区不卡| 久久久久久久久久久黄色| 亚洲精品国产一区| 第四色日韩影片| 色婷婷久久久久swag精品 | 白白操在线视频| 欧美gv在线| 欧美人与性动xxxx| 稀缺小u女呦精品呦| 欧洲美女日日| 欧美黑人国产人伦爽爽爽| 午夜影院免费在线观看| 狠狠色丁香九九婷婷综合五月| 超碰国产精品久久国产精品99| 欧美婷婷久久五月精品三区| 亚洲欧美日韩中文播放| 狠狠操精品视频| 网站一区二区| 国产一区二区三区免费视频| 免费无遮挡无码永久在线观看视频| 老司机久久99久久精品播放免费| 91网站在线免费观看| 台湾av在线二三区观看| 亚洲免费在线观看| 欧美人与性动交| 国产中文字幕二区| 国产成人精品一区二区三区在线 | 五月六月丁香婷婷| 性人久久久久| 欧美日韩成人在线视频| 久久久久久av无码免费看大片| 成人国产亚洲欧美成人综合网 | 日本老熟妇毛茸茸| 日本成人一区二区三区| 136国产福利精品导航| 你懂的av在线| 精品国产一级| 少妇高潮 亚洲精品| 看片网址国产福利av中文字幕| 狠狠狠色丁香婷婷综合激情| 欧美激情第六页| 国产白袜脚足j棉袜在线观看| 国产欧美日韩精品一区二区免费 | 欧美一区二区三区另类| 日本高清+成人网在线观看| 国产精品无码白浆高潮| 99久久伊人久久99| 少妇一晚三次一区二区三区| 日韩成人高清| 亚洲奶大毛多的老太婆| 国产成人自拍视频在线| 国产999精品久久久久久| 亚洲最新在线| 欧美日韩视频免费观看| 日韩精品视频在线观看网址 | 欧美成人日本| 成人黄色片在线| 久久99久久| 精品国产乱码久久久久久天美 | 国产精选久久| 久久精品国产亚洲一区二区| 亚洲天堂中文字幕在线| 国产女主播视频一区二区| 一本久道综合色婷婷五月| 婷婷亚洲精品| 欧美中文在线观看| 视频一区二区三区在线看免费看| 精品日本高清在线播放| 亚洲av成人无码一二三在线观看| 国内精品美女在线观看| 国产精品久久7| av资源一区| 亚洲国内精品视频| 国产精品黄色大片| 久久亚洲一区二区三区明星换脸| 极品美女扒开粉嫩小泬| 亚洲视频分类| 国产精品久久久久久av福利软件| 国产视频第一区| 精品视频全国免费看| 欧美性x x x| 国产精品一区不卡| 阿v天堂2018| 天堂日韩电影| 国产精品久久99久久| 日本精品在线| 欧美一级一级性生活免费录像| 538精品在线视频| 国产成人综合在线播放| 成年人午夜免费视频| 希岛爱理av免费一区二区| 欧美中文在线观看国产| 成年人在线视频免费观看| 欧美日韩成人综合在线一区二区| 一级性生活免费视频| 日本午夜一区二区| 久久久成人精品一区二区三区| 日韩精品成人在线观看| 欧美激情va永久在线播放| 黄色小视频免费观看| 亚洲成人手机在线| 91社区视频在线观看| 一区二区三区四区视频免费观看 | 亚洲成人激情在线| 免费看污视频的网站| 亚洲美女偷拍久久| 久久人人爽人人爽人人片 | 亚洲第一主播视频| 亚洲精品国产精品国自产网站| 久久se精品一区二区| 国产一线二线三线女| 欧美伦理影院| 成人免费看片网址| 精品国产黄a∨片高清在线| 欧美日韩国产成人在线| 成人精品一区二区| 欧美成人免费网站| 在线观看中文字幕网站| 亚洲高清免费观看| 我要看一级黄色录像| 91麻豆免费在线观看| 手机看片国产精品| 日韩黄色免费网站| 妞干网视频在线观看| 欧美影院三区| 久久精品国产一区二区三区日韩 | 欧美电影一区二区三区| 亚洲欧美在线视频免费| 亚洲欧美日韩成人高清在线一区| 91精彩刺激对白露脸偷拍| 国产精品一区二区不卡| www.精品在线| 欧美专区一区二区三区| 真实国产乱子伦对白视频| 久久精品国产68国产精品亚洲| 久久久久久草| 国产精品传媒| av一区二区在线看| 伊人久久大香线蕉综合影院首页| 日韩av观看网址| www.综合| 韩国欧美亚洲国产| 性欧美videoshd高清| 久久精品免费播放| 成年人视频在线看| 亚洲一区www| 青青操在线视频| 亚洲黄色在线观看| www三级免费| 91精品啪在线观看国产60岁| 夜夜躁日日躁狠狠久久av| 日本高清无吗v一区| 五月婷婷亚洲综合| 丁香五六月婷婷久久激情| 久久精品第一页| 一区二区三区丝袜| 欧美另类视频在线观看| 亚洲另类色综合网站| 欧美日韩黄色网| 成人免费小视频| 岛国片在线免费观看| 中文无字幕一区二区三区| 在线观看日本中文字幕| 久久久国产精品不卡| 欧美特黄一区二区三区| 国产日韩高清在线| 免费黄色片网站| 国产精品美女一区二区在线观看| 精品无码在线观看| 国产精品―色哟哟| 日韩三级久久久| 国产精品的网站| 国产精品老熟女一区二区| 亚洲特级片在线| 中文字幕手机在线观看| 亚洲午夜精品在线| 999这里只有精品| 日本高清不卡视频| 一卡二卡在线视频| 日韩精品一区二区三区四区视频| 国产91麻豆视频| 日韩成人在线播放| 国产女人在线观看| 久久精品亚洲热| 91九色在线播放| 奇米一区二区三区四区久久| 欧美free嫩15| 91九色国产视频| 91精品丝袜国产高跟在线| 久久av一区二区三区亚洲| 精品国产一区二区三区久久久蜜臀 | 精品一区二区在线看| 九九热99视频| 国产精品亚洲成人| 蜜臀av一区二区三区有限公司| 99国产精品久久久久久久久久| 国产精品扒开腿做爽爽| 中文字幕色av一区二区三区| 久青草视频在线观看| 色综合久久中文综合久久97 | 日韩欧美一区二区视频| 亚洲 国产 欧美 日韩| 中文亚洲视频在线| 日本天码aⅴ片在线电影网站| 2020久久国产精品| 亚洲天堂网站| 久久国产精品免费一区| 国产精品福利在线观看播放| 人妻夜夜添夜夜无码av| 三级精品在线观看| 91人人澡人人爽| 欧美极品aⅴ影院| 国产精品不卡av| 欧美视频日韩视频在线观看| 亚洲第一天堂网| 在线性视频日韩欧美| 都市激情久久综合| 国产深夜精品福利| 亚洲精品**不卡在线播he| 在线观看三级网站| 日韩高清一区在线| 800av在线播放| 亚洲精品国产视频| 中国一级特黄视频| 国产视频精品免费播放| 日韩av激情| 91日本在线视频| 不卡在线一区二区| 免费黄色福利视频| 成人做爰69片免费看网站| 久久人妻无码aⅴ毛片a片app| 色嗨嗨av一区二区三区| 色网站免费观看| 欧美成人黄色小视频| 成人久久网站| 你懂的视频在线一区二区| 黄色一区二区三区四区| 亚洲精品成人在线播放| 国产日本欧美一区二区| 国产又大又黄视频| 亚洲福利视频专区| 污污网站在线看| 91青草视频久久| 国产高清一区| 国产精品视频中文字幕| 久久久久国色av免费看影院| 国内免费精品视频| 欧美成va人片在线观看| 1区2区在线观看| 亚洲www永久成人夜色| 日韩在线理论| 亚洲精品久久久中文字幕| 国产欧美一区二区精品久导航| www.国产成人| 日韩精品极品视频| 原纱央莉成人av片| 久久偷看各类wc女厕嘘嘘偷窃 | 免费观看国产视频| 欧美日韩成人在线视频| 91成人入口| 欧美午夜小视频| 99re6这里只有精品视频在线观看| 国产精品7777| 日韩激情在线视频| 性欧美18~19sex高清播放| 九九九九九精品| 久久精品一区二区三区中文字幕| wwwwxxxx国产| 欧美性猛交一区二区三区精品 | 中文字幕va一区二区三区| 国产成人a v| 日韩有码视频在线| 久久在线观看| www.欧美黄色| 91在线看国产| 国产99免费视频| 色吧影院999| 欧美专区一区| 分分操这里只有精品| 久久久久一区二区三区四区| 懂色av蜜臀av粉嫩av喷吹| 久久精品欧美视频| 91久久偷偷做嫩草影院电| 国产av麻豆mag剧集| 久久色.com| 国产一区二区麻豆| 欧美精品久久久久久久| 夜色77av精品影院| 日韩一级免费片| 亚洲综合视频在线| 全色精品综合影院| 国产玖玖精品视频| 欧美~级网站不卡| 国产福利在线观看视频| 欧美性xxxxxxxx| 欧美bbbxxxxx| 日韩av高清| 国产凹凸在线观看一区二区| 亚洲欧美在线视频免费| 色综久久综合桃花网| 91精品国产自产在线丝袜啪 | 国产99精品视频| 无码人妻精品一区二| 久久精品久久久久电影| 欧美亚洲国产日韩| 天天干天天色天天干| 婷婷久久综合九色国产成人| av网站在线播放| 国产美女精品在线观看| 日韩成人av影视| 精品无码人妻一区二区三区| 国产亚洲精品美女| www.久久东京|