2019独角兽企业重金招聘Python工程师标准>>>

张大胖一上班,领导就扔了一个任务给他,把项目中的JavaScript代码做点“小小”的改变:

  • 1. 把 == 改为全等 ===
  • 2. 把parsetInt不标准的调用改为标准用法 parseInt(xxx)-> parseInt(xxx,10)

对不熟悉JS的同学稍微解释一下:

JS在比较两个变量的时候,双等号将执行类型转换; 三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false );

parseInt(a,10) 表示以十进制的方式来解析。

对于这些任务,张大胖脑海中马上闪现出了解决办法:字符串替换。

  • 对第一个任务: 找到'==',替换成'==='就行 。
  • 对第二个任务: parseInt(xxx) 改成parseInt(xxx,10), 没法直接替换,得写个正则表达式,找到那些只有一个参数的parseInt字符串,然后加上一个新的参数:10 。

张大胖对自己的正则表达式能力不太自信,如果考虑得不周全,代码就可能被改坏了。

有没有别的办法?

1、抽象语法树

使用正则表达式,只能把JavaScript源代码当做文本来处理,能力很弱,无法触及到JavaScript的语法层面,正则表达式没法知道这个地方是变量,那个地方是函数名.....

如果能把JavaScript源码转化成结构化的对象,就可以精确地知道一段代码中有哪些变量名,函数名,参数......  这样就可以写程序就可以进行处理了。

张大胖想起来自己没有考及格的《编译原理》,里边讲到了抽象语法树(AST)不就是所谓结构化的东西吗?

比如表达式 result = 6+7*3 , 用抽象语法树来表示就是:

如果把所有的JavaScript代码都转化成这样一颗AST的树,那代码的一切都尽在掌握, 可以任意修改了。

但是这其中有三个问题:

1. 怎么从文本形式的源代码形成这么一个AST ?

让自己写程序实现那就太难了,得做词法分析,语法分析等等。

2. 如何遍历这个AST,来修改这颗树的枝枝叶叶?    

比如我想在AST这棵树中添加一个新的节点,该怎么做?

3. 修改完成以后,怎么再次把AST变成文本的源代码? 

张大胖赶紧打开Google 搜索,很快便找到了三个开源的工具,正好完成对应的三个功能:

  • esprima : 从JavaScript源代码形成AST
  • estraverse:遍历树的节点并修改
  • escodegen : 把修改完的AST再次转化为源代码。

2、创建AST

说干就干,张大胖准备了一段代码来做实验:

//源码
function fun1(opt) {if (opt.status == 1) {console.log('1');}if (opt.status == 2) {console.log('2');}
}
function fun2(age) {if (parseInt(age) >= 18) {console.log('ok 你已经成年');}
}

使用esprima,轻轻松松就把它转化成了抽象语法树。

//JS语法树模块
const esprima = require('esprima');
//创建AST
const AST = esprima.parseScript(jsCode);

(由于转成树后结构非常大,这里不再展示了, 感兴趣的同学自己可以到 https://bit.ly/2SNIYJT 去玩一把, 很有趣。 )

比如: if (parseInt(age) >= 18)  这一句,就被转化成了这样:

3、遍历修改AST

有了AST,就可以就是遍历和修改了,还是使用开源的工具。

//JS语法树遍历各节点
const estraverse = require('estraverse');
//从JS语法树生成源代码
const escodegen = require('escodegen');function walkIn(ast){estraverse.traverse(ast, {enter: (node) => {toEqual(node);//把 == 改为全等 ===setParseInt(node); //parseInt(a)-> parseInt(a,10)}});
}

这个函数负责把‘==’改成‘===’

function toEqual(node) {if (node.operator === '==') {node.operator = '===';}
}

这个函数负责把parseInt改成标准调用:

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"});}
}

经过这个函数,原来的 if (parseInt(age) >= 18) 就变成了下图这样,相当于增加了一个节点,对应的代码就是 :if (parseInt(age,10) >= 18)

最后使用escodegen 把修改过的AST再次变成源代码,就大功告成了:

//生成目标代码
const code = escodegen.generate(ast);
//写入文件.....
//....你懂的

通过这个实验,张大胖基本上了解了AST的原理和用法,接下来可以着手正式的编程了。

4、总结

本文的例子用AST也许不是最优解, 主要是为了展示AST的处理技术, AST实际上就是源代码的一种结构化表示, 利用它及相关工具可以方便地优化和修改代码,只要是你能对这棵“AST树”做“修剪”就可以对源代码做各种“手脚”:

  • JavaScript代码语法、风格的检查
  • 在IDE中的错误提示、自动补全,重构
  • 代码的压缩和混淆 代码的转换 ......

有这么强大的功能,AST处理技术是很多知名工具的基础, 例如babel,webpack,还有jd taro等都把AST用得淋漓尽致。

Refer

[1] 没有AST, IDE中的错误提示、自动补全、重构、语法检查......都玩不转了
https://mp.weixin.qq.com/s/UYzwVRPFas6hwe2U7R0eIg

转载于:https://my.oschina.net/leejun2005/blog/64346

10 分钟入门 AST 代码优化与修改相关推荐

  1. 10分钟入门 ANSA API

      扫描下方二维码关注我的微信公众号 - CAE软件二次开发Lab阅读全文! 文章目录 10分钟入门 ANSA API Script Editor (脚本编辑器) Modules(模块) 在ANSA中 ...

  2. 教程 | 10分钟入门简笔画(哆啦A梦篇)

    多啦A梦主题曲 - 腾讯视频 从小就喜欢小叮当这个形象,不知道为什么后来改成了多啦A梦. 多啦A梦之歌,时常萦绕在耳旁: 心中有许多愿望,能够实现有多棒,只有哆啦a梦可以带着我实现梦想 可爱圆圆胖脸庞 ...

  3. 微信小程序怎么在wxml中插入多个图片_白云工商带你10分钟入门微信小程序开发...

    10分钟入门微信小程序开发 程序开发难吗? No!来白云工商带你10分钟入门微信小程序开发! 英语不好能学好程序开发吗? 当然能,程序开发常见的也就那几个单词,只要热爱,非常简单! 欣赏 首先,来欣赏 ...

  4. 教程 | 10分钟入门简笔画(彩色小插画)

    你好,色彩. BY:铃铛子 马克笔的笔触感觉: BY:铃铛子 由于大家写字的时候,为了笔锋,会起笔停顿一下,落笔再停顿一下,但是运行马克笔的时候不要这样,起笔确定了就画线,落笔除非必要,否则不停顿. ...

  5. 教程 | 10分钟入门简笔画12(创意小插画)

    我的一家 很多宝宝说想学创作,今天跟大家分享简笔画中的联想小插画. 所谓联想小插画,就是想到哪里画到哪里.首先确定一个主题,抓住主要元素,然后用装饰性字体.元素.花边来丰富画面.我尝试创作了一张My ...

  6. 教程 | 10分钟入门禅绕画 3

    禅绕装饰画是一种意识流装饰画,也是一种有趣随性的涂鸦,笔触可以天马星空随意走动. 禅绕画的构图技巧:重复.对称.均衡.重叠.勾线.肌理等.所有技巧的组合可以使得画面节奏和谐.疏密有度.节奏韵律恰如其分 ...

  7. 10分钟入门Pandas(添加一些个人见解)

    pandas十分钟入门(codedog翻译) 原文下载地址:https://download.csdn.net/download/qq_35189715/11392455 github地址:https ...

  8. SQL人的优势:实战大数据开发10分钟入门

    金色的九月,即将开启收获的篇章. 一早醒来,魔都湛蓝的天空,暑气未消的阳光,一扫前几日狂风暴雨的阴霾.品着自己煮的咖啡,吃上一口朱家角寄来的苏荷月饼,人生真赞! 这个礼拜将32G内存和M.2 SSD都 ...

  9. 教程 | 10分钟入门简笔画 11(简笔小插画入门)

    小插画的运用十分广泛,POP.手账中都可以运用到. 今天跟宝宝们分享几个入门级别的小插画.也许有人会提倡小插画直接动笔就画,但是我个人觉得为了最后的成品有保障,在不确定的地方可以定一下位,当然主体上还 ...

最新文章

  1. FGMap学习之--加载百度地图
  2. Flash和滚动字幕
  3. Linux快速工作使用指令经验(持续更新)
  4. 栈/队列 互相模拟实现
  5. 程序结束后去哪儿了?
  6. 【算法分析与设计】所有结点对的最短路径算法
  7. 十大经典排序算法3(Python版本)
  8. filter过滤器_Java过滤器Filter讲解(Java基础)
  9. 全新Linux+Python高端运维班第一次作业
  10. 信号处理电路整理(RC、运放、TTL)
  11. iOS-调用系统的短信和发送邮件功能,实现短信分享邮件分享
  12. 2022年最佳WordPress企业主题
  13. 当我谈跑步时我谈些什么
  14. 不是二进制MAT文件,请尝试执行 load -ASCII 以便以文本形式读取
  15. C语言学习笔记(kk-zkx)
  16. TVS管 与 稳压二极管参数对比
  17. CLCL-多重剪切板
  18. 智能时代为什么需要区块链技术?
  19. 四十二、Fluent欧拉模型流化床模拟
  20. HTML+CSS静态网页作业:NBA勒布朗詹姆斯篮球明星带js(5页)

热门文章

  1. ESP32-S的UART2的初始化需要注意的问题
  2. 关于工程教育和工程教育专业认证工作的思考——吴岩司长在工程教育高峰论坛上的讲话
  3. AI电磁组中的NN到底有什么优势?
  4. python函数只有被调用才会执行_Python函数调用
  5. linux mysql解锁账号密码忘了怎么办,linux下mysql忘记密码怎么办
  6. k8s 自定义dns_K8S 生态周报| Docker v19.03.10 正式发布
  7. 图书管理系统python代码课程设计报告_数据结构图书管理系统课程设计报告
  8. java如何给一个链表定义和传值_如何在CUDA中为Transformer编写一个PyTorch自定义层...
  9. matlab 两列数据相乘,在EXCEL中,两列完全相同的数据,求和结果不一样??单元格两列相乘的公式...
  10. nginx 没有cookie_Nginx 内容缓存及常见参数配置