这是一棵树嘛
直奔主题
笼统语法树是js代码另一种构造映照,可以将js拆解成AST,也可以把AST转成源代码。这中心的历程就是我们的用武之地。 运用 笼统语法树(AST) 可以对你的源代码举行修正、优化,以至可以打造本身的编译东西。实在有点相似babel的功用。
AST深邃的狠吓人?
AST很简朴,并没有你设想的那样深邃。许多处所都把这个手艺给强调了,什么编译道理,笼统语法树 光看这名字就以为吓人。固然一项手艺总归要起个名字,就像给本身的孩子取名字,肯定要起一个嵬峨上,深有寄意的名字。所以,名字只是一个代号。从名字来看就会让许多人望而生畏。然则ast超等简朴,然则功用超等壮大。
我们能用这个手艺做许多有意义的东西,只需你能想到的。
本文术道连系,让你感受到ast的风趣和简朴,今后爱上ast,还能依据本身的须要打造本身的编译器。
什么是AST?
ast全称是abstract syntax tree,翻译过来叫-笼统语法树。实在这含两个意义,一个是“笼统”,一个是“树”。笼统示意把js代码举行了构造化的转化,转化为一种数据构造。这类数据构造实在就是一个大的json对象,json我们都熟习,他就像一颗枝繁恭弘=叶 恭弘茂的树。
有树根,有树干,有树枝,有树恭弘=叶 恭弘.不管多小多大,都是一棵完全的树。
如何天生AST?
你可以大抵的想一下假如亲身完成把js代码转换成构造化的数据我们应当怎样做?
有点像小时候拆解本身的玩具,每一个零件之间都有着从属关系。
关于如何天生ast,我们能够会想到剖析js代码的划定规矩运用字符串处置惩罚、正则婚配等要领,假如对简朴的代码处置惩罚我们是可以完成的。然则假如可以对随便的一段代码举行处置惩罚那就须要斟酌异常多的状况。详细如何完成我们没必要过于纠结,这也不是重点。
但终究的完成里我们能想到要领基本都会被用到。我们可以简化邃晓,也就是对js代码经过了一系列的加工处置惩罚,变成了一堆零件或许食材(像老妈给我们做的香馥馥的饭菜,但条件是先预备佳肴)。
这个拆解的历程能够较为庞杂,所以我们须要用现成要领,直接拿过来用便可以了。
所以我们须要用到esprima、UglifyJS等库,做菜的食材有许多种,所以会存在许多如许的三方库,而我们会运用个中一种便可以了。
先运用esprima 种菜,体味一下
种子:
//源代码
function fun(a,b){
}
成熟:
{
"type": "FunctionDeclaration",//函数声明
"id": {
"type": "Identifier",//标识符
"name": "fun" //函数称号
},
"params": [//函数参数
{
"type": "Identifier",//参数标识符
"name": "a"//参数称号
},
{
"type": "Identifier",
"name": "b"
}
],
"body": {//函数体
"type": "BlockStatement",//语句块儿
"body": []//详细内容为空,因为是空要领
}
}
有了AST能做什么?
到这一步你已可以把js代码转换成一棵构造化的树了,那下一步要做什么呢? 比如在没有树的状况下,你要对代码里的某个代码举行替代。要把一切 console.log给解释掉或许删除,你能够会运用IDE的查找替代或许用node写一个要领,读取文件然后查找替代。
这类体式格局不够平安也不够科学,稍有不慎就会把代码给搞坏了。
但这个时候你有了构造化代码树,是否是只需对这棵树举行修修剪剪然后把这棵树转换成为js代码便可以了呢?
答案:肯定是可以的。因为树已发生了变化,修正了树就相当于修正了源码。
如何操纵这棵树呢?我想你应当已知道了,就是对这json对象举行操纵,要领就多了去了,条件是你得有一点点js基本。
又一个题目,如何把树再转成代码?
脑洞翻开,用递归加字符串拼接,这个要领应当是可以的。
然则这棵树不是你天生的,构造特性你并不清楚,不计其数个节点呢?怎样拼接?真要干,那能够得搞得流鼻血。
这就像是食材预备好了,转换成源码的历程就是炒菜的历程。详细的转源码的道理不多说,也没必要纠结。运用现成的要领便可以,所以要用到estraverse,escodegen这两个库。
estraverse 可以遍历树的一切节点,省去你对树的递归遍历
escodegen 可以把树再加工转成源代码
历程总结
到这里一直都没有提到任何代码,只是理论了一番,然则相信你已邃晓了ast以及ast的作用。然后在陈述历程当中引出了3个库,有了这三个库便可以对你的js代码举行多样化处置惩罚,只需你能想到的。
看图邃晓全部处置惩罚历程:
这个历程简朴,清楚,所以说ast简朴、风趣、好玩。因为现在代码可以被你恣意的践踏了。
实例运用
说的再清楚都不够直观,毕竟都是脑补,不如看代码来的直爽。
这里就拿一样平常编码中的一些小题目举例,来演示一下AST的运用。
- 把 == 改成全等 ===
- 把parsetInt不规范的挪用改成规范用法 parseInt(a)-> parseInt(a,10)
这里我运用esprima的官方东西天生了ast,东西地点http://esprima.org/demo/parse…
看下要处置惩罚的源码:
//源码
function fun1() {
console.log('fun1');
}
function fun2(opt) {
if (opt.status == 1) {
console.log('1');
}
if (opt.status == 2) {
console.log('2');
}
}
function fun3(age) {
if (parseInt(age) >= 18) {
console.log('ok 你已成年');
}
}
转成ast,因为转成树后构造异常大,所以这里我只贴了一部分,你也可以到东西页面本身天生下。
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "fun1"
},
"params": [],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"computed": false,
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
}
},
"arguments": [
{
"type": "Literal",
"value": "fun1",
"raw": "'fun1'"
}
]
}
}
]
},
"generator": false,
"expression": false,
"async": false
}
]
}
ast看上去构造庞杂,盯着细致看后基本都能看懂。一切的代码都在特定的节点内里。详细的这里就不引见了,可以到上面的东西地点去视察差别的ast构造。总之这就是一个对象,只需你能对这个对象举行修正、增添、删除即可。
最先完成以上功用
init
//引入东西包
const esprima = require('esprima');//JS语法树模块
const estraverse = require('estraverse');//JS语法树遍历各节点
const escodegen = require('escodegen');//JS语法树反编译模块
//获�取代码ast
const AST = esprima.parseScript(jsCode);
/**
*
* @param {遍历语法树} ast
*/
function walkIn(ast){
estraverse.traverse(ast, {
enter: (node) => {
toEqual(node);//把 == 改成全等 ===
setParseint(node); //parseInt(a)-> parseInt(a,10)
}
});
}
2.把 == 改成全等 ===
/**
* 设置全等
*/
function toEqual(node) {
if (node.operator === '==') {
node.operator = '===';
}
}
- 把parseInt改成规范挪用
/**
* 把parseint改成规范要领
* @param {节点} node
*/
function setParseint(node) {
//推断节点范例 要领称号,要领的参数的数目,数目为1就增添第二个参数
if (node.type === 'CallExpression' && node.callee.name === 'parseInt' && node.arguments.length===1){
node.arguments.push({//增添参数,实在就是数组操纵
"type": "Literal",
"value": 10,
"raw": "10"
});
}
}
//天生目的代码
const code = escodegen.generate(ast);
//写入文件.....
//....你懂的
代码不多,需求简朴,但已充足能申明全部处置惩罚历程以及ast的壮大。 ast的节点许多,有些缭乱,送你一首歌【汪峰的无所谓】,操纵的时候只需体贴你本身的需求便可以,不须要对一切的节点都搞邃晓。按需处置惩罚便可以。
AST手艺的运用
虽然日常平凡用不到ast,但又时候都在运用ast手艺。众所周知、无人不知的babel,webpack,另有jd taro等都把ast用的极尽描摹,脱离了ast他们就跪了。
AST这么简朴,好没手艺含量
AST没有手艺含量吗?怎样能够呢,假如真这么以为怕是会被笑掉大牙的。假如仅仅停留在运用层面的话,邃晓到这步已基本可以了,只需是你能对这棵树做修剪便可以对源代码做手脚。
别的ast如何天生的?如何把ast转换成源码的?这就有点深邃了。会运用就像是在山脚下能看到的景致有限,邃晓了背地道理机制就像是爬上了山顶,别样的景致一览无余。不过上不上山看个人兴致,有兴致的同砚可以去看源码、做研讨,这里就不再多说,因为我也不知道。哈哈哈
总结
本文重要引见了
什么是ast:
ast实在就把js代码举行笼统为一种json构造;
ast的用处:
运用ast可以轻易的优化和修正代码,还能打造本身的编译器;
然后经由过程详细的示例演示了如何操纵ast,终究是愿望你能对ast有一个体系全局的熟悉和邃晓并可以运用ast打造本身的编译东西。
演示代码下载,迎接star
https://github.com/bigerfe/fo…
自家看法,迎接打脸
原创不容易,请多勉励