前言

AST抽象语法树想必大家都有听过这个概念,但是不是只停留在听过这个层面呢。其实它对于编程来讲是一个非常重要的概念,当然也包括前端,在很多地方都能看见AST抽象语法树的影子,其中不乏有vue、react、babel、webpack、typeScript、eslint等。简单来说但凡需要编译的地方你基本都能发现AST的存在。

babel是用来将javascript高级语法编译成浏览器能够执行的语法,我们可以从babel出发来了解AST抽象语法树。

如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新文章~

babel编译流程

了解AST抽象语法树之前我们先来简单了解一下babel的编译流程,以及AST在babel编译过程中起到了什么作用?

我这里画了张图方便理解babel编译的整个流程

  • parse: 用于将源代码编译成AST抽象语法树
  • transform: 用于对AST抽象语法树进行改造
  • generator: 用于将改造后的AST抽象语法树转换成目标代码

很明显AST抽象语法树在这里充当了一个中间人的身份,作用就是可以通过对AST的操作还达到源代码到目标代码的转换过程,这将会比暴力使用正则匹配要优雅的多。

AST抽象语法树

在计算机科学中,抽象语法树(Abstract Syntax Tree,AST) 是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

虽然在日常业务中我们可能很少会涉及到AST层面,但如果你想在babelwebpack等前端工程化上有所深度,AST将是你深入的基础。

预览AST

说了这么多,那么AST到底长什么样呢?

接下来我们可以通过工具AST Explorer来直观的感受一下!

比如我们如下代码:

let fn = () => {
console.log('前端南玖')
}

它最终生成的AST是这样的:

  • AST抽象语法树是源代码语法结构的一种抽象表示
  • 每个包含type属性的数据结构,都是一个AST节点
  • 它以树状的形式表现编程语言的语法结构,每个节点都表示源代码中的一种结构

AST结构

为了统一ECMAScript标准的语法表达。社区中衍生出了ESTree Spec,是目前前端所遵循的一种语法表达标准。

节点类型

类型 说明
File 文件 (顶层节点包含 Program)
Program 整个程序节点 (包含 body 属性代表程序体)
Directive 指令 (例如 "use strict")
Comment 代码注释
Statement 语句 (可独立执行的语句)
Literal 字面量 (基本数据类型、复杂数据类型等值类型)
Identifier 标识符 (变量名、属性名、函数名、参数名等)
Declaration 声明 (变量声明、函数声明、Import、Export 声明等)
Specifier 关键字 (ImportSpecifier、ImportDefaultSpecifier、ImportNamespaceSpecifier、ExportSpecifier)
Expression 表达式

公共属性

类型 说明
type AST 节点的类型
start 记录该节点代码字符串起始下标
end 记录该节点代码字符串结束下标
loc 内含 line、column 属性,分别记录开始结束的行列号
leadingComments 开始的注释
innerComments 中间的注释
trailingComments 结尾的注释
extra 额外信息

AST是如何生成的

一般来讲生成AST抽象语法树都需要javaScript解析器来完成

JavaScript解析器通常可以包含四个组成部分:

  • 词法分析器(Lexical Analyser)
  • 语法解析器(Syntax Parser)
  • 字节码生成器(Bytecode generator)
  • 字节码解释器(Bytecode interpreter)

词法分析

这里主要是对代码字符串进行扫描,然后与定义好的 JavaScript 关键字符做比较,生成对应的Token。Token 是一个不可分割的最小单元。

词法分析器里,每个关键字是一个 Token ,每个标识符是一个 Token,每个操作符是一个 Token,每个标点符号也都是一个 Token,词法分析过程中不会关心单词与单词之间的关系.

除此之外,还会过滤掉源程序中的注释和空白字符、换行符、空格、制表符等。最终,整个代码将被分割进一个tokens列表

javaScript中常见的token主要有:

关键字:var、let、const等
标识符:没有被引号括起来的连续字符,可能是一个变量,也可能是 if、else 这些关键字,又或者是 true、false 这些内置常量
运算符: +、-、 *、/ 等
数字:像十六进制,十进制,八进制以及科学表达式等
字符串:变量的值等
空格:连续的空格,换行,缩进等
注释:行注释或块注释都是一个不可拆分的最小语法单元
标点:大括号、小括号、分号、冒号等

比如我们还是这段代码:

let fn = () => {
console.log('前端南玖')
}

它在经过词法分析后生成的token是这样的:

工具:esprima

[
{
"type": "Keyword",
"value": "let"
},
{
"type": "Identifier",
"value": "fn"
},
{
"type": "Punctuator",
"value": "="
},
{
"type": "Punctuator",
"value": "("
},
{
"type": "Punctuator",
"value": ")"
},
{
"type": "Punctuator",
"value": "=>"
},
{
"type": "Punctuator",
"value": "{"
},
{
"type": "Identifier",
"value": "console"
},
{
"type": "Punctuator",
"value": "."
},
{
"type": "Identifier",
"value": "log"
},
{
"type": "Punctuator",
"value": "("
},
{
"type": "String",
"value": "'前端南玖'"
},
{
"type": "Punctuator",
"value": ")"
},
{
"type": "Punctuator",
"value": "}"
}
]

拆分出来的每个字符都是一个token

语法分析

这个过程也称为解析,是将词法分析产生的token按照某种给定的形式文法转换成AST的过程。也就是把单词组合成句子的过程。在转换过程中会验证语法,语法如果有错的话,会抛出语法错误。

还是上面那段代码,在经过语法分析后生成的AST是这样的:

工具:AST Explorer

{
"type": "VariableDeclaration", // 节点类型: 变量声明
"declarations": [ // 声明
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier", // 标识符
"name": "fn" // 变量名
},
"init": {
"type": "ArrowFunctionExpression", // 箭头函数表达式
"id": null,
"generator": false,
"async": false,
"params": [], // 函数参数
"body": { // 函数体
"type": "BlockStatement", // 语句块
"body": [
{
"type": "ExpressionStatement", // 表达式语句
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"identifierName": "console"
},
"name": "console"
},
"computed": false,
"property": {
"type": "Identifier",
"name": "log"
}
},
"arguments": [ // 函数参数
{
"type": "StringLiteral", // 字符串
"extra": {
"rawValue": "前端南玖",
"raw": "'前端南玖'"
},
"value": "前端南玖"
}
]
}
],
"directives": []
}
}
}
],
"kind": "let" // 变量声明类型
}

在得到AST抽象语法树之后,我们就可以通过改造AST语法树来转换成自己想要生成的目标代码。

常见的解析器

第一个用JavaScript编写的符合EsTree规范的JavaScript的解析器,后续多个编译器都是受它的影响

一个小巧、快速的 JavaScript 解析器,完全用 JavaScript 编写

babel官方的解析器,最初fork于acorn,后来完全走向了自己的道路,从babylon改名之后,其构建的插件体系非常强大

UglifyJS 是一个 JavaScript 解析器、缩小器、压缩器和美化器工具包。

esbuild是用go编写的下一代web打包工具,它拥有目前最快的打包记录和压缩记录,snowpack和vite的也是使用它来做打包工具,为了追求卓越的性能,目前没有将AST进行暴露,也无法修改AST,无法用作解析对应的JavaScript。

AST应用

了解完AST,你会发现我们可以用它做许多复杂的事情,我们先来利用@babel/core简单实现一个移除console的插件来感受一下吧。

这个其实就是找规律,你只要知道console语句在AST上是怎样表现的就能够通过这一特点精确找到所有的console语句并将其移出就好了。

  • 先来看下console语句的AST长什么样

很明显它是一个表达式节点,所以我们只需要找到name为console的表达式节点删除即可。

  • 编写plugin
const babel  = require("@babel/core")
let originCode = `
let fn = () => {
const a = 1
console.log('前端南玖')
if(a) {
console.log(a)
}else {
return false
}
}
` let removeConsolePlugin = function() {
return {
// 访问器
visitor: {
CallExpression(path, state) {
const { node } = path if(node?.callee?.object?.name === 'console') {
console.log('找到了console语句')
path.parentPath.remove()
}
}
}
}
} const options = {
plugins: [removeConsolePlugin()]
}
let res = babel.transformSync(originCode, options) console.dir(res.code)

从执行结果来看,它找到了两个console语句,并且都将它们移除了

这就是对AST的简单应用,学会AST能做的远不止这些像前端大部分比较高级的内容都能看到它的存在。后面会继续更新Babel以及插件的用法。

原文首发地址点这里,欢迎大家关注公众号 「前端南玖」,如果你想进前端交流群一起学习,请点这里

我是南玖,我们下期见!!!

从Babel开始认识AST抽象语法树的更多相关文章

  1. AST抽象语法树

    抽象语法树简介 (一)简介 抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并 ...

  2. AST抽象语法树 Javascript版

    在javascript世界中,你可以认为抽象语法树(AST)是最底层. 再往下,就是关于转换和编译的"黑魔法"领域了. 现在,我们拆解一个简单的add函数 function add ...

  3. 用python演示一个简单的AST(抽象语法树)

    如果对'a + 3 * b'进行解释,当中a=2,b=5 代码非常easy,就不再进行具体的解释了. Num = lambda env, n: n Var = lambda env, x: env[x ...

  4. 【深入】 - AST抽象语法树

    参考: https://segmentfault.com/a/1190000016231512

  5. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  6. 如何查看SparkSQL 生成的抽象语法树?

    前言 在<Spark SQL内核剖析>书中4.3章节,谈到Catalyst体系中生成的抽象语法树的节点都是以Context来结尾,在ANLTR4以及生成的SqlBaseParser解析SQ ...

  7. 理解Babel是如何编译JS代码的及理解抽象语法树(AST)

    Babel是如何编译JS代码的及理解抽象语法树(AST) 1. Babel的作用是?   很多浏览器目前还不支持ES6的代码,但是我们可以通过Babel将ES6的代码转译成ES5代码,让所有的浏览器都 ...

  8. Babel(抽象语法树,又称AST)

    文章:https://juejin.im/post/5a9315e46fb9a0633a711f25 https://github.com/jamiebuilds/babel-handbook/blo ...

  9. 五分钟了解抽象语法树(AST)babel是如何转换的?

    抽象语法树 什么是抽象语法树? It is a hierarchical program representation that presents source code structure acco ...

  10. 抽象语法树(AST)

    AST描述 在计算机科学中,抽象语法树(AST)或语法树是用编程语言编写的源代码的抽象语法结构的树表示.树的每个节点表示在源代码中出现的构造.语法是“抽象的”,因为它不代表真实语法中出现的每个细节,而 ...

随机推荐

  1. PHP配置负载均衡

    我项目是用宝塔面板.所以这次用宝塔面板演示. 环境: LNMP 代码:2套.2套代码除了配置其他都是一样 域名:1个.2级域名.其实一级二级都没关系 /************************ ...

  2. 『现学现忘』Git分支 — 38、Git分支介绍

    目录 1.Git分支简介 2.Git分支与SVN分支的区别 3.工作中为什么要使用分支 4.Git分支管理的好处 1.Git分支简介 几乎所有的版本控制系统都以某种形式支持分支. 使用分支意味着,你可 ...

  3. 齐博x1如何录制阿里等第三方直播流

    暂时只支持windows录制,可以在你本地电脑进行录制体验.点击下面的网址,下载录制程序文件,直接解压到任何一个目录都可以.里边同时包含自建直播服务器的功能,不用自建直播流的话,就可以忽视相关配置即可 ...

  4. CJK备注

    pip清华镜像库 :pip install XXX -i https://pypi.tuna.tsinghua.edu.cn/simple pip阿里巴巴镜像库:pip install XXX -i ...

  5. 一键部署haproxy脚本

    HAPROXY_VERSION=2.6.6 HAPROXY_FILE=haproxy-${HAPROXY_VERSION}.tar.gz #HAPROXY_FILE=haproxy-2.2.12.ta ...

  6. pycharm安装第三方的包

    这里以安装selenium为例子 在file下找到settings 在弹出的窗口点击python interpreter ,然后在右侧点击[+] 在弹窗顶部输入要导入的包名,在下方列表找到对应包后,点 ...

  7. 用 VS Code 搞 Qt6:让信号和槽自动建立连接

    Qt 具备让某个对象的信号与符合要求的槽函数自动建立连接.弄起来也很简单,只要调用这个静态方法即可: QMetaObject::connectSlotsByName(...); connectSlot ...

  8. TASK 总结

    信相连知识 1.python操作EXCEL 库:xlwings. 基本操作:打开.读写.关闭. 2.python操作问题库 库:JIRA 基本操作:提交问题 3.网页信息在网址不变时的获取 库:req ...

  9. MongoDB导入导出备份数据

    需要提前安装mongodb-database-tools参考:centos离线安装mongodb-database-tools 导出数据 常用的导出有两种:mongodump和mongoexport, ...

  10. RobotFrameWork基础一

    1.变量: 作用域: Set Global Variables:设定全局级变量 Set Suite Variables: 设定Test Suite 级变量              Set Test ...