阅读原文
在使用前端许多工具插件的时候,我们大多知道每个工具库、每个插件能做什么,不过很多同学其实并不清楚背后用到的技术,如webpack、rollup、UglifyJS、Lint等很多的工具和库的核心都是通过Abstract Syntax Tree 抽象语法树这个概念来实现对代码的检查、分析等操作的。通过了解抽象语法树这个概念,你也可以随手编写类似的工具,发现一个新的世界。
理论的知识总是有些枯燥乏味,不过客官别急,一步一步来。
其实这些工具的原理都是通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作。
wikipedia定义:
In computer science, an abstract syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language.
翻译为:
在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。
Javascript的语法是为了给开发者更好的编程而设计的,但是不适合程序的理解。所以需要转化为AST来更适合程序分析,浏览器编译器一般会把源码转化为AST来进行进一步的分析等其他操作。
以下只介绍Javascript相关的抽象语法树
比如说有一段代码:
var a = 3;
a + 5;
那么它的抽象语法树就类似:
JavaScript Parser,把js源码转化为抽象语法树的解析器。
浏览器会把js源码通过解析器转为抽象语法树,再进一步转化为字节码或直接生成机器码。
一般来说每个js引擎都会有自己的抽象语法树格式,Chrome的v8引擎,firefox的SpiderMonkey引擎等等,MDN提供了详细SpiderMonkey AST format的详细说明,算是业界的标准。
发展到现在可能不同的JavaScript Parser的AST格式会不同,或基于SpiderMonkey AST format,或重新设计自己的AST format,或基于SpiderMonkey AST format优化改进。通过优化抽象语法树,来使程序运行的更快,也是一种提高效率的方法。
常用的JavaScript Parser有:
Shift
在Esprima的官网有一个比较各个Parser解析速度的列表 Speed Comparison 。 看下来Acorn是公认的最快的。
UglifyJS2的作者自己实现了一套js的抽象语法树,用到了继承,和现有的扁平的抽象语法树都有所不同,但作者也提供使用不同的抽象语法树来解析代码。
AST explorer 可以在线看到不同的parser解析js代码后得到的AST。
JavaScript AST visualizer 可以在线可视化的看到AST。
通过 esprima
, 把一个名字为ast的空函数的源码生成一颗AST树:
var esprima = require('esprima');
var code = 'function ast(){}';
var ast = esprima.parse(code);
生成的抽象语法树长这样:
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "ast",
"range": [
9,
12
]
},
"params": [],
"body": {
"type": "BlockStatement",
"body": [],
"range": [
14,
16
]
},
"generator": false,
"expression": false,
"range": [
0,
16
]
}
],
"sourceType": "module",
"range": [
0,
16
]
}
通过 estraverse
遍历并且更新抽象语法树,把函数名称改为ast_awsome:
...
var estraverse = require('estraverse');
estraverse.traverse(ast, {
enter: function (node) {
node.name += "_awsome";
}
});
通过 escodegen
将AST重新生成为源码:
...
var escodegen = require("escodegen");
var regenerated_code = escodegen.parse(ast)
AST三板斧:
esprima
把源码转化为AST estraverse
遍历并更新AST escodegen
将AST重新生成源码 浏览器最先就会把源码解析为抽象语法树,对浏览器而言AST的作用非常重要。
对开发者而言,AST的作用就是可以精准的定位到代码的任何地方,它就像是是你的手术刀,对代码进行一系列的操作。
常见的几种用途:
抽象语法树在前端领域中的应用广泛,通过抽象语法树大家可以实现很多功能,发现编写工具提高效率带来的乐趣。