AST抽象语法树(Abstract Syntax Tree)

1,265 阅读3分钟

抽象语法树(Abstract Syntax Tree)

webpackLint等很多的工具和库的核心都是通过Abstract Syntax Tree抽象语法树这个概念来实现对代码的检查、分析等操作的

  • 通过了解抽象语法树这个概念,你也可以随手编写类似的工具

抽象语法树用途

  • 代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码错误提示、代码自动补全等等
    • 如JSLint、JSHint对代码错误或风格的检查,发现一些潜在的错误
    • IDE的错误提示、格式化、高亮、自动补全等等
  • 代码混淆压缩
    • UglifyJS2等
  • 优化变更代码,改变代码结构使达到想要的结构
    • 代码打包工具webpack、rollup等等
    • CommonJS、AMD、CMD、UMD等代码规范之间的转化
    • CoffeeScript、TypeScript、JSX等转化为原生Javascript

抽象语法树定义

这些工具的原理都是通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作

在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。

Javascript的语法是为了给开发者更好的编程而设计的,但是不适合程序的理解。所以需要转化为AST来使之更适合程序分析,浏览器编译器一般会把源码转化为AST来进行进一步的分析等其他操作。 JavaScript Parser


  • JavaScript Parser,把js源码转化为抽象语法树的解析器。

  • 浏览器会把js源码通过解析器转为抽象语法树,再进一步转化为字节码或直接生成机器码。

  • 一般来说每个js引擎都会有自己的抽象语法树格式,Chrome的v8引擎,firefox的SpiderMonkey引擎等等,MDN提供了详细SpiderMonkey AST format的详细说明,算是业界的标准。

常用的JavaScript Parser

  • esprima
  • traceur
  • acorn
  • shift

4.2 esprima

接下来我们将下面代码转换成AST,然后生成先的函数newAst

let code = 'function ast(){}'; //js代码即可(字符串形式)

第一步:先安装 esprima estraverse escodegen

npm  i esprima estraverse escodegen -D

把上面的源码转成AST

// 把源码转化为AST
let ast = esprima.parseModule(code);
console.log(ast);

打印结果如下 Module { type: 'Program', body: [ FunctionDeclaration { type: 'FunctionDeclaration', id: [Object], params: [], body: [Object], generator: false, expression: false, async: false } ], sourceType: 'module' }

接下来,遍历AST 并更新AST

let indent = 0;
function padding() {
  return " ".repeat(indent);
}

estraverse.traverse(ast, {
  enter(node) {
    //进入

    console.log(padding() + node.type + "进入");
    if (node.type === "FunctionDeclaration") {
      node.id.name = "newAst";
    }
    indent += 2;
  },
  leave(node) {
    //离开
    indent -= 2;
    console.log(padding() + node.type + "退出");
  },
});


打印结果
Program进入
  FunctionDeclaration进入
    Identifier进入
    Identifier退出
    BlockStatement进入
    BlockStatement退出
  FunctionDeclaration退出
Program退出

最后,把修改后的抽象语法重新生成源代码

//把修改过后的抽象语法树重新生成源代码
let result = escodegen.generate(ast);
console.log(result);
打印后的结果
function newAst() {
}