这是一棵树嘛

直奔主题

抽象语法树是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的使用。

  1. 把 == 改为全等 ===
  2. 把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 = '===';}
}
  1. 把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...

自家观点,欢迎打脸

原创不易,请多鼓励

从现在起-彻底学会 js ast相关推荐

  1. 分分钟学会 JS AST,打造自己的编译器

    关注"重度前端" 以自己的观点说透一个技术,最好不过带给你启发 仅此而已 助力前端深度学习 ━━━━ 这是一棵树嘛 直奔主题 抽象语法树是js代码另一种结构映射,可以将js拆解成A ...

  2. 10分钟学会js处理json常用方法

    一.json定义 JSON ( JavaScript Object Notation) ,它是一串字符串 只不过元素会使用特定的符号标注. {} 双括号表示对象 [] 中括号表示数组 "&q ...

  3. 一个demo学会js

    全栈工程师开发手册 (作者:栾鹏) 快捷链接: js系列教程1-数组操作全解 js系列教程2-对象和属性全解 js系列教程3-字符串和正则全解 js系列教程4-函数与参数全解 js系列教程5-容器和算 ...

  4. 30分钟学会js新特性

    JavaScript新特性 准备工作 安装node和nodemon 12.16.3版本的node已经逐渐支持es2015以上的新特性 nodemon的作用是监听文件代码的变动,自动重启 下载方式 cn ...

  5. 半小时,一篇文0基础让你轻松学会JS翻书效果

    如何使用制作翻书效果? 思路:   首先准备一个div作为书本,然后准备一个div作为第一页,其中还要有两个div作为正反面,分别对其各部分属性进行设置.其中还要为book与page开启3D,还用到了 ...

  6. 5分钟学会js上传图片校验图片格式、大小、尺寸宽高

    js上传图片校验图片格式.大小.尺寸宽高 一.前言        js上传图片校验图片格式.大小.尺寸宽高.        在此记录下,分享给大家. 二.代码 <input type=" ...

  7. 一小时学会js正则表达式

    正则表达式 regular expression : RegExp 用来处理字符串的规则 只能处理字符串 它是一个规则:可以验证字符串是否符合某个规则(test),也可以把字符串中符合规则的内容捕获到 ...

  8. 推荐一个朋友 - 学历不好,非科班,不负光阴终进大厂

    今天给大家推荐一位圈内的朋友,网名:zz_jesse,这家伙真是个骚年.虽然他没有好学历(双非渣本),并且也非科班出身,但最终靠着自己不断的努力和坚持,不断提升自己的专业能力和水平,最终走入大厂.目前 ...

  9. js怎么给div加滑动条 vue给弹出层加滑动条(一分钟学会用js加滚动条)

    @[TOC](怎么给div加滑动条 给弹出层加滑动条(一分钟学会js加滚动条)) 给div加滑动条 给需要滑动条的div加一个样式 加一个高度,加一个overflow:auto; overflow:a ...

最新文章

  1. 常用eclipse插件集合, 持续更新
  2. 不一样的随机数生成方法(C/C++)
  3. firewall-cmd命令管理防火墙
  4. python区块链开发_Fabric区块链Python开发详解
  5. [转]Shared——回调函数是什么
  6. QT UI获得控件ID(HWND)
  7. POJ - 2893 M × N Puzzle(n*m数码问题+逆序对结论)
  8. 前端学习(3233):高阶函数函数柯里化案例
  9. 小程序作用域与模块化
  10. 4.openSession() 、 getCurrentSession()与session上下文
  11. 计算机主机放电,电脑需要放电才能开机_电脑主板放电才能开机
  12. 计算机网络常用端口号大全
  13. ppt背景图片怎么设置?6步教你快速搞定!
  14. 【BZOJ3717】[PA2014]Pakowanie 状压DP
  15. 【热点资讯】哪所英国大学最适合你?
  16. 未闻花名ED《君がくれたもの》原文+罗马音+中文
  17. meta分析 1. Risk Ratio
  18. 数据库主键、外键和唯一键的区别
  19. fiilt1左耳无法同步_【FIIL T1 蓝牙耳机使用总结】功能|操作|闪连|防水|音质_摘要频道_什么值得买...
  20. BI的作用,体现在企业的哪些方面

热门文章

  1. 超线程/双核/双路CPU三者的区别
  2. mysql xact abort_XACT_ABORT 用法
  3. linux忽略abort信号,ARM Linux Data Abort 异常处理流程
  4. python问答系统实践
  5. Jenkins+Gradle+Gitlab+蒲公英 +打包成功后发送邮件配置
  6. 从新手小白入门MFC框架-黄强-专题视频课程
  7. python中的绝对值符号怎么打_如何在python中取绝对值
  8. IDF实验室之牛刀小试啥?
  9. NOIWC2018 游记
  10. MFC EnableWindow() 启用和禁用控件(设置个控件或窗口可用/不可用),ShowWindow()使某个控件或窗口(显示/不显示)可见/不可见