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

这是一棵树嘛
直奔主题
抽象语法树是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 种菜,体会一下
种子:

成熟:

有了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.html
看下要处理的源码:

转成ast,由于转成树后结构非常大,所以这里我只贴了一部分,你也可以到工具页面自己生成下。


ast看上去结构复杂,盯着仔细看后基本都能看懂。所有的代码都在特定的节点里面。具体的这里就不介绍了,可以到上面的工具地址去观察不同的ast结构。总之这就是一个对象,只要你能对这个对象进行修改、添加、删除即可。
开始实现以上功能
init

2.把 == 改为全等 ===

3. 把parseInt改成标准调用

代码不多,需求简单,但已足够能说明整个处理过程以及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/zdqd-demos
自家观点,欢迎打脸
原创不易,请多鼓励

30分钟学会JS AST,打造自己的编译器的更多相关文章
- 【grunt第二弹】30分钟学会使用grunt打包前端代码(02)
前言 上一篇博客,我们简单的介绍了grunt的使用,一些基础点没能覆盖,我们今天有必要看看一些基础知识 [grunt第一弹]30分钟学会使用grunt打包前端代码 配置任务/grunt.initCon ...
- 30分钟学会使用Spring Web Services基础开发
时隔一年终于又推出了一篇30分钟系列,上一篇<30分钟学会反向Ajax>是2016年7月的事情了.时光荏苒,岁月穿梭.虽然一直还在从事Java方面的开发工作,但是私下其实更喜欢使用C++. ...
- 30 分钟学会 Flex 布局
30 分钟学会 Flex 布局 有酒 617 人赞同了该文章 为什么我要写这一篇关于 Flex 布局的教程? 因为它十分简单灵活,区区简单几行代码就可以实现各种页面的的布局,以前我在学习页面布局的 ...
- 30分钟学会XAML
1.狂妄的WPF 相对传统的Windows图形编程,需要做很多复杂的工作,引用许多不同的API.例如:WinForm(带控件表单).GDI+(2D图形).DirectX API(3D图形)以及流媒体和 ...
- 30分钟学会如何使用Shiro
本篇内容大多总结自张开涛的<跟我学Shiro>原文地址:http://jinnianshilongnian.iteye.com/blog/2018936 我并没有全部看完,只是选择了一部分 ...
- 教你30分钟学会XAML
1.狂妄的WPF 相对传统的Windows图形编程,需要做很多复杂的工作,引用许多不同的API.例如:WinForm(带控件表单).GDI+(2D图形).DirectX API(3D图形)以及流媒体和 ...
- 30分钟学会Objective-C
注: 本文首发于我的个人博客:https://evilpan.com/2019/04/05/objc-basics/ 请原谅我的标题党.但是如果你有其他语言的学习经验,要学习Objective-C的语 ...
- 30分钟学会Docker里面开启k8s(Kubernetes)登录仪表盘(图文讲解)
前言 我们之前搭建了第一个docker项目: windows环境30分钟从0开始快速搭建第一个docker项目(带数据库交互):https://www.cnblogs.com/xiongze520/p ...
- 【grunt整合版】30分钟学会使用grunt打包前端代码
grunt 是一套前端自动化工具,一个基于nodeJs的命令行工具,一般用于:① 压缩文件② 合并文件③ 简单语法检查 对于其他用法,我还不太清楚,我们这里简单介绍下grunt的压缩.合并文件,初学, ...
随机推荐
- Android使用7牛云存储
第一次使用这个云存储,话说7牛云存储大有来头!区别于国内外其他云存储,七牛自行研发的全分布式架构解决了其他云存储单一数据中心架构可能存在的风险,同时首创双向加速特性对数据上传下载均加速,使得数据访问速 ...
- orcale 之函数
我们知道存储过程的调用是一条 PL/SQL 语句.那么对于一些表达式我们用什么呢?这里函数的功能就会体现出来了. 函数和过程在创建方式上有很多的类似地方,也是编译后放入内存中以供用户使用,只不过函数调 ...
- Dictionary<string, object>不区分大小写
Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIg ...
- Django settings.py添加静态文件夹
我们需要一个静态文件夹来存放文件,例如jQuery的模块 <script src="statics/jquery-3.2.1.js"></script> 引 ...
- Codeforces 809D. Hitchhiking in the Baltic States
Description 给出 \(n\) 个数 \(a_i\),每一个数有一个取值 \([l_i,r_i]\) ,你来确定每一个数,使得 \(LIS\) 最大 题面 Solution 按照平时做法,设 ...
- Tool Scripts
1. Function: 16进制转字符串 Create FUNCTION [dbo].[f_hextostr] (@hexstring VARCHAR(max)) RETURNS VARCHAR(m ...
- unity 中 protobuff 的用法 一句话攻略
(一)unity 添加 pb 的 dll 支持.1,从GitHub上下载protobuf源码 (源码链接:https://github.com/google/protobuf),找到 csharp 分 ...
- 使用Having子句
Having 子句与where子句的功能类似,都是对行进行筛选.但是,where搜索条件是在分组操作之前对记录进行筛选,然后再由group BY 对筛选后符合条件的行进行分组:而Having搜索条件则 ...
- .Net程序员玩转Android系列之三~快速上手
快速环境搭建和Hello World 第一步:JAVA SDK(JDK)的安装: 官方下载地址: http://www.oracle.com/technetwork/java/javase/downl ...
- linux环境的基本搭建
1.准备Linux环境(我的是centos系统) 如果你是hadoop用户在使用sudo之前需要配置一下:获取sudo权限 切换到root vi /etc/sudoersroot ALL=(ALL) ...