一、什么是抽象語法樹
開發者可以閱讀,理解按語法規則書寫的的代碼,但是對編譯器等工具來講,它能理解的就是抽象語法樹(Abstract Syntax Tree)。即按語法規則書寫的源代碼,能被編譯器理解的抽象結構。它以樹狀的形式表現編程語言的語法結構。
大致了解編譯原理:
二、抽象語法樹生成及組成
1、抽象語法樹的生成: 以function add(a, b){ return a+b}為例
A、分詞:將源碼分割成語法單元
B、語義分析: 分詞結果之上分析這些語法單元之間的關系
組成:
首先add函數語法塊是一個FunctionDeclaration對象,撤分為三塊:
1、函數名稱即ID :add
2、兩個params : [a,b]
3、一個body塊: 即 { return a + b}
ID 無法再拆,params 拆為 兩個Identifier組成的數組,便無法拆除,body拆除:
body為一個BlockStatement對象: {return a+b},其中包含一個ReturnStatement對象: return a+b,繼續打開包含一個BinaryExpression對象: a+b,由三部分組成left(a),operator(+),right(b),body無法再拆。及由這寫拆分類內容組成抽象語法樹。
pragram拆分圖:
AST直觀圖:
recastAST結構:
三、AST涉及范圍
1、AST in TS 和 ESlint
TS在大型js運用程序中,對開發者理解當前應用,程序運行穩定性及維護,和迭代項目的源碼越來越重要。簡單介紹一下關於TS中編譯器與AST。
以 var a = 1為例,在ts編譯器中parser階段輸出AST:
在內部,ts編譯器將使用Parser生成AST來提供編譯程序時的類型檢測,我們也可以使用AST在TS之上開發代碼美化工具、代碼格式化工具、代碼分析工具等。eslint原理類似,只是不同的parser工具轉換成AST樹形結構可能不同。 ESLint是一個用來檢查和報告JavaScript編寫規范的插件化工具,eslint 檢測基於AST除了這些內置規則外,eslintAPI中,我們可利用源代碼生成的AST,開發自定義插件和自定義規則。
本地自定義eslint規則:
2、AST in Common.js
Common.js 是模塊化標准,node.js實現了這一標准,node.js
采用了模塊規范。其模塊化原理: 將js代碼字符串解析為AST樹,然后遍歷AST樹分析出require的依賴項,使用我們自己寫得require函數來加載依賴項即可。具體可參考browserify轉換模塊,裝載模塊。
3、 AST in 打包工具(webpack)
Webpack是一個JavaScript生態的打包工具,其打出bundle結構是一個IIFE(立即執行函數),結構如下
(function(module){})([function(){},function(){}]);
Webpack 打包流程也需要AST支持,借助acron庫解析源碼,生成AST,提取相關模塊依賴關系。其中支持treeShaking,使打包輸出中去除沒有引用的模塊,有效減少包體積。
4、AST in others
AST可支持其他相關庫:
a、Codemod(自動化修改代碼庫):如vscode自動保存格式化代碼。
b、roollup等打包工具
c、JSX等轉換為原生js等
d、Babel,js墊片等
四、AST recast 自定義實現箭頭函數npm包實踐
安裝recast (npm i recast --dev-save)
具體代碼如下:
五、AST猜想,與性能極致最求
1、極致性能追求,二進制AST
在大型js應用中,解析AST所需時間成為了應用性能瓶頸。二進制AST設想通過WebAssembly AST 所用的一些策略來提高解析 JavaScript AST 的性能。
二進制AST設想為js引入一種新的網絡傳輸格式,該格式提供了AST的二進制編碼,以此提升js性能。當前流行webpack rooloup等構建工具,及ts、babel這樣的編譯器可直接輸出二進制AST,因此和這種提案十分吻合。
方案可解決問題:
a、在需要的地方無法獲取信息,如變量提升,內置方法
b、需要對每個js文件進行欲解析
c、js 語法將表達式編譯為何種類型的字符級歧義
完成提案:解析過程將比創建完整的AST所需時間減少70%-90%。
相關鏈接:
https://github.com/xitu/gold-miner/blob/master/TODO/binary-ast-newsletter-1.md