[源碼-webpack01-前置知識] AST抽象語法樹

導航

[深刻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] Hooksjavascript

[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIhtml

[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程前端

前置知識

一些單詞

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 對象文檔vue

從javascript程序到機器可執行的機器碼須要經歷三個階段

  • 語法檢查:詞法分析,語法分析
  • 編譯運行
  • 總結:詞法分析 -> 語法分析 -> 編譯運行

AST

abstract syntax tree:抽象語法樹
abstract:抽象的java

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
  • ( 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/parser ) 是 Babel 中使用的 JavaScript 解析器
    • 解析過程分爲兩個階段
      • 語法分析:字符流 -> token流
      • 詞法分析:token流 -> AST
    • @babel/parser
  • 轉換 transform
    • @babel/traverse:主要用於遍歷AST
      • Babel接收解析獲得的AST並經過 ( babel-traverse ) 對其進行 ( 深度優先遍歷 )
      • 在此遍歷過程當中對節點進行 ( 添加 )、( 更新 ) 及 ( 移除 ) 操做
      • traverse:是遍歷的意思
    • @babel/types:主要用來操做AST,好比 ( 添加 )、( 更新 ) 及 ( 移除 ) 操做
      • 除了手動替換,可使用@babel/types更加房便快捷
      • 至關於做用於 AST 的類 lodash 庫
    • @babel/traverse
    • @babel/types
  • 生成 generate
    • @babel/generator:來將轉換後的抽象語法樹轉化爲Javascript 字符串
      • 將通過轉換的AST經過babel-generator再轉換爲js代碼
      • 過程及時深度遍歷整個AST,而後構建轉換後的代碼字符串。
    • @babel/generator

@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…node

babel轉換 juejin.im/post/5dca1e…
babel轉換案例 cloud.tencent.com/developer/a…
babel插件介紹 zhuanlan.zhihu.com/p/61780633react

相關文章
相關標籤/搜索