導航
[深入01] 執行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件迴圈
[深入05] 柯里化 偏函式 函式記憶
[深入06] 隱式轉換 和 運算子
[深入07] 瀏覽器快取機制(http快取機制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模組化
[深入13] 觀察者模式 釋出訂閱模式 雙向資料繫結
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[react] Hooks
[部署01] Nginx
[部署02] Docker 部署vue專案
[部署03] gitlab-CI
[原始碼-webpack01-前置知識] AST抽象語法樹
[原始碼-webpack02-前置知識] Tapable
前置知識
一些單詞
abstract:抽象的
( abstract syntax tree:抽象語法樹 )
Identifier:識別符號
Punctuator :標點符號
declaration:宣告
VariableDeclaration:變數宣告
declarator:宣告人
traverse:遍歷
expression:表達,表示式
performance:效能
// while parseExpression() tries to parse a single Expression with performance in mind.
// 而parseExpression()會嘗試在考慮效能的情況下解析單個Expression
doubt:疑惑
// When in doubt, use .parse()
// 如果有疑惑的情況,請使用.parse()而不要使用.parseExpression()
Numeric:數字
複製程式碼
一些網站
AST explorer 檢視原始碼對應的AST和JSON結構
esprima 可以檢視詞法分詞階段生成的tokens
AST 視覺化工具 檢視AST視覺化樹狀圖
AST 物件文件
從javascript程式到機器可執行的機器碼需要經歷三個階段
- 語法檢查:詞法分析,語法分析
- 編譯執行
- 總結:詞法分析 -> 語法分析 -> 編譯執行
AST
abstract syntax tree:抽象語法樹
abstract:抽象的
AST應用場景
- 程式碼 ( 語法檢測 ),程式碼 ( 風格檢測 ),程式碼 ( 格式化 ),程式碼 ( 高亮 ),程式碼 ( 錯誤提示 ),程式碼 ( 自動補全 )
- eslint amd cmd
- webpack 通過 babel 轉義 js 語法
AST解析過程
- (1) 讀取js檔案中的 ( 字元流 )
- (2) 通過 ( 詞法分析 ) 生成 token ----------- 詞法分析也叫掃描scanner,分詞階段,token是一維陣列
- (3) 通過 ( 語法分析 ) 生成 AST ------------- 語法分析也叫解析器
- (4) 生成 ( 機器碼 ) 執行 -------------------- 編譯階段也叫編譯器
詞法分析
- 詞法分析是將 ( 字元流char stream ) 轉換為 ( 記號流token stream )
- token 是不可分割的最小單元,是一個一維陣列
- ( 詞法分析 ) 也稱之為 ( 掃描scanner )
- ( 詞法分析器 ) 裡的每一個 ( 關鍵字,識別符號,操作符,標點符號,字串,數字,布林值,註釋符,空白字元,空格,換行符 ) 等都是一個token
- token陣列中,每一個物件包含 ( type ) 和 ( value )
- type
- value
- 常見的 ( type ) 如下:
- Keyword (關鍵詞)
- Identifier (識別符號)
- Punctuator (標點符號)
- Numeric(數字)
- String (字串)
- Boolean(布林)
- Null(空值)
- 最終程式碼被分割進一個tokens列表,即一維陣列
原始碼1:
const add = (a, b) => {
return a + b
}
tokens:
[
{ "type": "Keyword", "value": "const" },
{ "type": "Identifier", "value": "add" },
{ "type": "Punctuator", "value": "=" },
{ "type": "Punctuator", "value": "(" },
{ "type": "Identifier", "value": "a" },
{ "type": "Punctuator", "value": "," },
{ "type": "Identifier", "value": "b" },
{ "type": "Punctuator", "value": ")" },
{ "type": "Punctuator", "value": "=>" },
{ "type": "Punctuator", "value": "{" },
{ "type": "Keyword", "value": "return" },
{ "type": "Identifier", "value": "a" },
{ "type": "Punctuator", "value": "+" },
{ "type": "Identifier", "value": "b" },
{ "type": "Punctuator", "value": "}" }
]
---
原始碼2:
const a = 1;
tokens:
[
{ "type": "Keyword","value": "const" },
{ "type": "Identifier","value": "a" },
{ "type": "Punctuator","value": "=" },
{ "type": "Numeric","value": "1" },
{ "type": "Punctuator","value": ";" }
]
說明:
(1) tokens是具有type,value屬性的物件組成的陣列
(2) token是詞法分析的最小單元,不能再分解
(3) 常見的type
- keyword關鍵字
- identfier識別符號
- punctuator標點符號
- Numeric:數字
複製程式碼
語法分析
- ( 語法分析 ) 會將詞法分析得出的token轉化成 ( 有語法含義 ) 的 ( 抽象語法樹 ) 結構
- 同時 ( 驗證語法 ),有語法錯誤則丟擲語法錯誤
- 屬性
- 最外層包含:
- ( type )
- ( sourceType )
- ( start )
- ( end ) 等
- ( body )
- body:是一個 ( 陣列 ) ,包含多個 ( 內容塊物件 statement ),每個內容塊包含
- type
- start
- end
- kind
- declarations:乘裝變數內容的塊,這個塊也是一個陣列,因為變數宣告可能宣告多個
- type
- start
- end
- id
- type
- start
- end
- name
- body:是一個 ( 陣列 ) ,包含多個 ( 內容塊物件 statement ),每個內容塊包含
- 最外層包含:
- ( statement - body陣列中的物件 ) 有很多型別,比如說變數宣告,函式定義,if語句,while迴圈,等都是一個statement
- VariableDeclaration:變數宣告
- FunctionDeclaration:函式定義
- IfStatement:if語句
- WhileStatement:while迴圈
原始碼:
var a = 1;
AST
{
"type": "Program",
"start": 0,
"end": 12,
"body": [ // ---------------------------------------------- body表示程式碼具體的內容
{ // ---------------------------------------------------- statement內容塊物件,一個body可能包含多個statement
"type": "VariableDeclaration", // --------------------- 變數宣告
"start": 0,
"end": 10,
"declarations": [
{
"type": "VariableDeclarator", // ------------------ 變數宣告
"start": 4,
"end": 9,
"id": {
"type": "Identifier", // ------------------------- 識別符號
"start": 4,
"end": 5,
"name": "a"
},
"init": {
"type": "Literal",
"start": 8,
"end": 9,
"value": 1,
"raw": "1"
}
}
],
"kind": "var" // --------------------------------------- 變數型別
}
],
"sourceType": "module"
}
說明:
(1) 最外層屬性:type,start,end,body[],sourceType
- body:表示程式碼的具體內容
- 內容塊:body中可能包含多個內容塊,每個內容塊用一個物件表示
- 內容塊包含:
- type
- start
- end
- kind
- declarations:乘裝變數內容的塊,這個塊也是一個陣列,因為變數宣告可能生命多個
- type
- start
- end
- id
- type
- start
- end
- name
- sourceType:表示語言的種類
(2) body是一個陣列,成員是statement內容塊物件,因為body可以包含多個statement內容塊
- statement 有很多型別,比如說變數宣告,函式定義,if語句,while迴圈,等都是一個statement
- VariableDeclaration:變數宣告
- FunctionDeclaration:函式定義
- IfStatement:if語句
- WhileStatement:while迴圈
複製程式碼
Babel原理
- babel編譯的過程:解析parse -> 轉換transform -> 生成generate
- 解析 parse
- @babel/parser:將字串轉換成AST,Babylon 是 Babel 中使用的 JavaScript 解析器。
- 解析過程分為兩個階段
- 語法分析:字元流 -> token流
- 詞法分析:token流 -> AST
- @babel/parser
- 轉換 transform
- @babel/traverse:允許你瀏覽、分析和修改抽象語法樹
- Babel接收解析得到的AST並通過 ( babel-traverse ) 對其進行 ( 深度優先遍歷 )
- 在此過程中對節點進行 ( 新增 )、( 更新 ) 及 ( 移除 ) 操作
- traverse:是遍歷的意思
- @babel/traverse
- @babel/traverse:允許你瀏覽、分析和修改抽象語法樹
- 生成 generate
- @babel/generator:來將轉換後的抽象語法樹轉化為Javascript 字串
- 將經過轉換的AST通過babel-generator再轉換為js程式碼
- 過程及時深度遍歷整個AST,然後構建轉換後的程式碼字串。
- @babel/generator
- @babel/generator:來將轉換後的抽象語法樹轉化為Javascript 字串
@babel/parser
babelParser.parse(code, [options]) ------------------------------------ 解析所有程式碼
babelParser.parseExpression(code, [options]) -------------------------- 解析單個表示式
引數:
- code:表示原始碼字串
- options:配置物件,可選
- allowImportExportEverywhere:預設import和export宣告只能出現在頂部,當此選項為true則可以出現在任何地方
- ...
複製程式碼
@babel/traverse
- 因為 ( @babel/parser解析 ) 和 ( @babel/generator生成 ) 基本不會變化,所以重點是 ( @babel/traverse轉換 )
import * as babylon from "babylon";
import traverse from "babel-traverse";
// 原始碼string
const code = `function square(n) {
return n * n;
}`;
// 解析 parse:string -> ast
const ast = babylon.parse(code);
// 轉換 transform:ast -> modified ast
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x"; // ---------------- 如果是識別符號並且識別符號的名字是n,就把n改為x
}
}
複製程式碼
@babel/generator
import {parse} from '@babel/parser';
import generate from '@babel/generator';
const code = 'class Example {}';
const ast = parse(code);
const output = generate(ast, { /* options */ }, code);
複製程式碼
babel轉化程式碼案例
- 需求:將小寫變數轉換成大寫
// 輸入
const numberFive = 5;
// 輸出
const NUMBERFIVE = 5;
複製程式碼
- 實現過程
安裝
@babel/core ----------------------- babel核心模組
@babel/parser --------------------- 字元流 -> token流 -> AST
@babel/traverse ------------------- AST -> modified AST
@babel/generator ------------------ modified AST -> 字元流
npm install @babel/core @babel/parser @babel/traverse @babel/generator -S
複製程式碼
程式碼
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
// 原始碼字串
const code = `
const nubmerFive = 5
`;
// 解析
let AST = parser.parse(code)
// 轉換
traverse(AST, {
enter(path) {
console.log(path.node.type, 'path.node.type')
if (path.node.type === 'Identifier') { // 如果node型別是識別符號,就將name轉成大寫形式
path.node.name = path.node.name.toUpperCase()
}
}
})
// 生成
const outputObj = generator(AST)
const outputStr = outputObj.code;
console.log(outputStr, 'outputStr')
複製程式碼
資料
AST babel-AST相關工具 juejin.im/post/5dca1e…
AST 從babel講到AST juejin.im/post/5ab35c…
AST 99%的人不瞭解的AST segmentfault.com/a/119000001…
AST 抽象語法樹-圖形 juejin.im/post/5bff94…
AST 具體細節:segmentfault.com/a/119000001…
AST cheogo.github.io/learn-javas…
AST詳細 www.codercto.com/a/88752.htm…
babel轉換 juejin.im/post/5dca1e…
babel轉換案例 cloud.tencent.com/developer/a…