[源码-webpack01-前置知识] AST抽象语法树

导航

[react] Hooks

[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 享元模式 组合模式 代理模式

[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] koa
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick

[源码-react01] ReactDOM.render01
[源码-react02] 手写hook调度-useState实现

[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[数据结构和算法01] 二分查找和排序

[深入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
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[深入24] Fiber
[深入25] Typescript

[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson
[前端学java08-SpringBoot实战总结1-7] 阶段性总结
[前端学java09-SpringBoot实战] 多模块配置 + Mybatis-plus + 单多模块打包部署
[前端学java10-SpringBoot实战] bean赋值转换 + 参数校验 + 全局异常处理
[前端学java11-SpringSecurity] 配置 + 内存 + 数据库 = 三种方式实现RBAC
[前端学java12-SpringSecurity] JWT
[前端学java13-SpringCloud] Eureka + RestTemplate + Zuul + Ribbon

前置知识

一些单词

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
  • <font color=red> webpack 通过 babel 转义 js 语法 </font>

AST解析过程

  • (1) 读取js文件中的 ( <font color=red>字符流</font> )
  • (2) 通过 ( <font color=red>词法分析</font> ) 生成 token ----------- 词法分析也叫扫描scanner,分词阶段,token是一维数组
  • (3) 通过 ( <font color=red>语法分析</font> ) 生成 AST ------------- 语法分析也叫解析器
  • (4) 生成 ( <font color=red>机器码</font> ) 执行 -------------------- 编译阶段也叫编译器

词法分析

  • 词法分析是将 ( <font color=red>字符流char stream</font> ) 转换为 ( <font color=red>记号流token stream</font> )
  • <font color=red>token 是不可分割的最小单元,是一个一维数组</font>
  • ( 词法分析 ) 也称之为 ( 扫描scanner )
  • ( <font color=red>词法分析器</font> ) 里的每一个 (<font color=red> 关键字,标识符,操作符,标点符号,字符串,数字,布尔值,注释符,空白字符,空格,换行符</font> ) 等都是一个token
  • <font color=blue>token数组中,每一个对象包含 ( type ) 和 ( value )</font>
  • type
  • value
    • 常见的 ( type ) 如下:
    • <font color=blue>Keyword (关键词)</font>
    • <font color=blue>Identifier (标识符)</font>
    • <font color=blue>Punctuator (标点符号)</font>
    • <font color=blue>Numeric(数字)</font>
    • <font color=blue>String (字符串)</font>
    • <font color=blue>Boolean(布尔)</font>
    • <font color=blue>Null(空值)</font>
  • 最终代码被分割进一个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:数字

语法分析

  • ( <font color=red>语法分析</font> ) 会将词法分析得出的token转化成 ( <font color=red>有语法含义</font> ) 的 ( <font color=red>抽象语法树</font> ) 结构
  • 同时 ( <font color=red>验证语法</font> ),有语法错误则抛出语法错误
  • 属性
    • 最外层包含:
      • ( <font color=red>type</font> )
      • ( <font color=red>sourceType</font> )
      • ( start )
      • ( end ) 等
      • ( <font color=red>body</font> )
        • body:是一个 ( 数组 ) ,包含多个 ( <font color=red>内容块对象 statement </font> ),每个内容块包含
          • type
          • start
          • end
          • kind
          • <font color=red>declarations</font>:乘装变量内容的块,这个块也是一个数组,因为变量声明可能声明多个
            • type
            • start
            • end
            • <font color=red>id</font>
              • type
              • start
              • end
              • <font color=red>name</font>
  • ( 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循环
image
image
image

Babel原理

  • babel编译的过程:<font color=red>解析parse -> 转换transform -> 生成generate</font>
  • 解析 parse
    • <font color=red>@babel/parser</font>:将字符串转换成AST,Babylon( 现在是@babel/parser ) 是 Babel 中使用的 JavaScript 解析器
    • 解析过程分为两个阶段
      • 语法分析:字符流 -> token流
      • 词法分析:token流 -> AST
    • @babel/parser
  • 转换 transform
    • <font color=red>@babel/traverse</font>:主要用于遍历AST
      • Babel接收解析得到的AST并通过 ( babel-traverse ) 对其进行 ( 深度优先遍历 )
      • 在此遍历过程中对节点进行 ( 添加 )、( 更新 ) 及 ( 移除 ) 操作
      • traverse:是遍历的意思
    • <font color=red>@babel/types</font>:主要用来操作AST,比如 ( 添加 )、( 更新 ) 及 ( 移除 ) 操作
      • 除了手动替换,可以使用@babel/types更加房便快捷
      • 相当于作用于 AST 的类 lodash 库
    • @babel/traverse
    • @babel/types
  • 生成 generate
    • <font color=red>@babel/generator</font>:来将转换后的抽象语法树转化为Javascript 字符串
      • 将经过转换的AST通过babel-generator再转换为js代码
      • 过程及时深度遍历整个AST,然后构建转换后的代码字符串。
    • @babel/generator
image
image

@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相关工具 https://juejin.im/post/6844903992762318855
AST 从babel讲到AST https://juejin.im/post/6844903581582098446
AST 99%的人不了解的AST https://segmentfault.com/a/1190000016231512
AST 抽象语法树-图形 https://juejin.im/post/6844903727451602951
AST 具体细节:https://segmentfault.com/a/1190000016231512
AST https://cheogo.github.io/learn-javascript/201709/runtime.html
AST详细 https://www.codercto.com/a/88752.html

babel转换 https://juejin.im/post/6844903992762318855
babel转换案例 https://cloud.tencent.com/developer/article/1444654
babel插件介绍 https://zhuanlan.zhihu.com/p/61780633

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容