应用ast抽象语法树修改js函数
原理:AST抽象语法树
目标:在每一个函数里面插入一个console.log()把函数传入的全部参数输出出来
关于:本文章是在基于我的个人理解且怕忘记知识所记录下来的给自己看并且分享自己的一个心得,文章可能有不严谨的地方,若有发现,可告知,勿喷
目录
- 准备工作
- 环境安装
- 使用recast
3.1. recast互转
3.2. recast遍历 - 实战:给每个方法加一个console.log输出这个方法的全部参数
准备工作
- node.js
- vscode
- recast(一个可以把js拆来拆去的东西,这是我们这篇博客的主角)
- ast explorer(一个可以在线把代码转换成ast语法树的网站,支持多种编程语言多种转换框架),由于我们使用的是recast来进行拆解,我们需要把这个网站里面的一个类型选择为recast
环境安装
初始化node.js环境
npm init -y
使用npm安装recast
npm i recast
然后创建一个index.js文件
直接引入这玩意
// module引入方式
import recast from 'recast'
由于上面的引入用的是module的引入方式,但是node.js默认使用的是commonjs的引入方式
// commonjs引入方式
const recast = require('recast')
我个人比较喜欢用module这种引入方式,需要在初始化完毕node.js环境后根目录下自动生成的一个package.json文件里面加入一句话,“type”: “module”。整篇文章,将会使用module的引入方式
接下来创建一个code.js文件,用于放置我们要准备解析的js代码。
然后我们利用node.js自带的fs(文件操作模块),对代码进行读取
const mCode = fs.readFileSync('code.js', { encoding: 'utf-8' })
到这里,我们的基本拆解环境就好咯。
使用recast
// 把代码转换成ast语法树
const mAst = recast.parse(mCode)
由于在编辑器里输出查看不是特别方便,我后面将会全部选择使用ast explorer解析网站进行解析
// 把ast树转换成代码重新输出
console.log(recast.print(mAst).code)
recast.visit(ast, methods)
这一个参数没啥好说的,直接把解析出来的ast传进去就好了
第二个参数就比较重要了,需要传进去的参数就是遍历类型(这边翻到了一个人整理的常见ast类型,跳转地址: JavaScript 常见 AST 梳理),是recast把一堆ast类型给封装成自带的一些ast类型方法,在recast遍历ast树的时候,我们只需要拿一个这个ast类型然后在前面加上visit就完事了,那我们如何得到这种类型?
有两种方法
第一种:去在线解析网站里面拿
配合ast explorer进行遍历
直接把代码丢到左边的框框里面,然后要看什么地方直接鼠标选取就完事了,然后红色箭头指的地方就是类型,这个类型是Identifier类型,在recast里面,这个类型方法就叫做visitIdentifier
第二种:直接翻源代码
如果你英语好的话,也可以直接进入这个recast的源代码里面翻那个类型方法表,就是右边那个密密麻麻的那堆东西,看着挺烦的我还是选择老老实实的去那个在线解析网站里面拿
既然我们拿到这个ast类型,那我们就可以开始遍历这个树了,那我们就把,每个方法的方法名字遍历下来吧。先去查一下方法名字对应的ast类型。
recast.visit(mAst, {visitIdentifier({ node }) {console.log(node.name)return false}
})
效果如下
细心的小伙伴会发现,为什么我的类型方法里面,多了个return false,而且,我只有两个方法,为什么遍历出来了好多好多name呢?
return false是每个类型方法必带的,不然会报错
遍历出来好多的name是因为,你的方法类型选错了喔
通过在线解析网站,我们可以看到,这个方法是一个方法类型FunctionDeclaration
他有多少个方法,就有多少个FunctionDeclaration,而这个里面就包含着方法的名字,那我们不就是直接遍历这个方法类型,然后取里面的name就好了喔
诶,这不就完美的输出了两个方法的名字了吗
但是,我们发现,有一个方法的名字,非常的粗鲁,我们需要把这个名字,变得健康,诶,那直接改对象就可以咯,先判断一下这个方法的名字,然后把这个名字改成hello,最后组合上去就好咯
诶,是不是很方便呢,嘿嘿,感受到ast语法树的快乐了吧,接下来开始我们的正文时间。
实战
在上面,我们已经把两个方法的名字全部输出来了,同时,我们还实现了修改了某个方法的名字
那我们要更骚一点,我们要直接给这个方法多加一行代码
老样子,上解析网站,为了更方便解析,我把code代码改成
function abc(a, b) {alert(1)
}function cnm(sda,asdas) {return 1
}
在解析网站中,我们发现,一个调用方法他是一个小快快,是在方法的body里面的另一个以数组形式存在的,对应的成员的ast类型是ExpressionStatement,我们不难发现,我们每多一行调用方法,他都会出现一个ExpressionStatement类型的成员,不难看出,我们只需要构造一个这个东西就能造一行调用代码了,那就废话不多说,直接开造
首先我们分析一下一个调用函数是由啥玩意构成的
{type: 'ExpressionStatement', // ast表达式类型expression: {type: 'CallExpression', // ast引用函数类型callee: { // 该函数的一些信息,比如名字啥的都在里面type: 'Identifier', // 函数的标志符name: '函数名字'}},arguments: [ // 函数参数,这玩意是一个数组{type: 'Literal', // 直接输入值的类型,也是一个ast类型,还有一个就是Identifier类型value: '参数名称'}... //以此类推的加]
}
分析完毕,我们把这个东西,封装成一个方法,方便我们直接调用
function astFunction(options) {return {type: 'ExpressionStatement',expression: {type: 'CallExpression',callee: {type: 'Identifier',name: options.name || 'astFunction'},arguments: options.params || []}}
}
效果如下,我是直接把函数给封装成一个js文件来直接使用,这样子看着代码不会太过于臃肿,我们想要的效果是不是已经实现了,嘿嘿,但是,你发现吧,我们还差一个条件,就是传入参数
那我们就继续观察解析网站,这不,在函数类型里面,就看到了这个a和b的参数数组,那我们不就直接仿造就好了嘿嘿
我把这个也封装成了一个方法,方便调用
function astParams(options) {return {type: 'Identifier',name: options.name || 'astParam'}
}
测试成功
诶,但是我们这个hello方法没有这个a和b参数呀,那我们要怎么办,嘿嘿,不慌,既然我们知道了这个参数的构成,那我们直接去取不就好了
这回,我们就需要用到一个es6语法的运算符了
... // 三点运算符,能把一个数组或者对象展开
由于params参数是一个数组形式,那我们直接用三点运算符整进去就好啦
诶,大功告成喔
index.js的代码
import recast from 'recast'
import {astFunction,astParams
} from './src/astAdd/index.js'
import fs from 'fs'
const mCode = fs.readFileSync('code.js', { encoding: 'utf-8' })
const mAst = recast.parse(mCode)
console.log(mAst)
recast.visit(mAst, {visitFunctionDeclaration({ node }) {// 给粗鲁的代码改名字if (node.id.name === 'cnm') {node.id.name = 'hello'}// 给每个方法加一个console.log并且输出参数node.body.body.unshift(astFunction({name: 'console.log',params: [...node.params]}))// node.body.push()return false}
})
// 组合代码
console.log(recast.print(mAst).code)
简单封装的几个函数代码
export function astFunction(options) {return {type: 'ExpressionStatement',expression: {type: 'CallExpression',callee: {type: 'Identifier',name: options.name || 'astFunction'},arguments: options.params || []}}
}export function astValueParams(options) {return {type: 'Literal',value: options.name || 'astParam'}
}export function astParams(options) {return {type: 'Identifier',name: options.name || 'astParam'}
}
路过的点个赞吗
ヽ(✿゚▽゚)ノ
应用ast抽象语法树修改js函数相关推荐
- JS实现AST抽象语法树问题
前端中的AST抽象语法树问题 四则运算 正则表达式 词法分析 语法分析 完整代码 github地址: https://github.com/feddiyao/Frontend-05-Template/ ...
- AST(抽象语法树)实战入门:js逆向中滑块加密if语句转化
概述:AST 抽象语法树 实战 入门 案例 js逆向 js滑块 js加密 极验 瑞数 阿里滑块 5秒盾 引言: AST算得上是高端技能.如果把爬虫技能分为初中高三个阶段的话.常规的JS逆向找找参数, ...
- AST(抽象语法树)超详细
自己研究的东西会用到AST,就自己通过查阅资料,整理一下. 本文目录 第一部分:AST的作用 第二部分:AST的流程 第三部分: Eclipse AST的获取与访问 第一部分:AST的作用 首先来一个 ...
- AST抽象语法树的基本思想
AST抽象语法树的基本思想 前言 AST概述 AST结构 AST解析 转换 生成 前言 在阅读java ORM框架spring data jpa的源码时,发现Hibernate(spring data ...
- php ast 抽象语法树,AST抽象语法树的基本思想
AST抽象语法树的基本思想 前言 AST概述 AST结构 AST解析 转换 生成 前言 在阅读java ORM框架spring data jpa的源码时,发现Hibernate(spring data ...
- ast抽象语法树_新抽象语法树(AST)给 PHP7 带来的变化
本文大部分内容参照 AST 的 RFC 文档而成:https://wiki.php.net/rfc/abstract_syntax_tree,为了易于理解从源文档中节选部分进行介绍. 我的官方群点击此 ...
- java AST 表达式_java 编译时注解-AST 抽象语法树简介
AST 语法入门 以前使用 Lombok 一直觉得是一个很棒的设计,可以同时兼顾注解的遍历和运行的性能. 运行时注解一直因为性能问题被人诟病. 自己尝试写过一些框架,但是耗费了比较多的精力,因为 AS ...
- python compiler.ast_ast --- 抽象语法树 — Python 3.7.9 文档
ast --- 抽象语法树¶ ast 模块帮助 Python 程序处理 Python 语法的抽象语法树.抽象语法或许会随着 Python 的更新发布而改变:该模块能够帮助理解当前语法在编程层面的样貌. ...
- Java解析SQL生成语法树_Atitit.sql ast 表达式 语法树 语法 解析原理与实现 java php c#.net js python...
Atitit.sql ast 表达式 语法树 语法 解析原理与实现java php c#.net js python 1.1.Sql语法树ast如下图锁死 2.SQL语句解析的思路和过程 2.1.le ...
最新文章
- 无聊中安装的MAC OS截图欣赏
- 浏览Github必备的5款神器级别的Chrome插件
- Python读取写入yaml文件
- C/C++中volatile关键字的作用
- Eigen官网教程(7)geometry几何模块的实践
- 第二阶段冲刺—第一天
- spring配置线程池
- [原]奇怪的参数错误
- c语言中dot作用,编程小组功能介绍及使用说明
- android中表情功能实现,android sina 微博表情功能的实现
- Python小白的数学建模课-11.偏微分方程数值解法
- P1505 [国家集训队]旅游 (树链剖分)
- 树莓派Python实现相机控制,定时与画面变化捕捉拍照
- 投资理财之基金四:指数基金
- 相见恨晚的编程学习词典!谁还不是南极滑冰的那个崽儿?!
- 目前三款国内最靠谱最良心的杀毒软件推荐 | 国内杀毒软件哪个好用?威航软件园诚意推荐这三个
- matlab产生泊松分布
- 由键盘输出三个整数分别存入变量num1,num2,num3,进行排序从小到大输出
- Delphi FastReport4.5安装说明
- 德鲁克《卓有成效的管理者》学习读书-总结