关注它,不迷路。
本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!
1.什么是纯函数
根据chatGPT-4的解释:
在JavaScript中,纯函数是一种特殊类型的函数,它具有以下两个主要特征:
确定性:对于相同的输入值,纯函数总是产生相同的输出。这意味着函数的输出完全依赖于输入参数,并且没有其他外部因素(如全局变量、数据库状态、文件系统等)影响输出。
无副作用:纯函数执行时不会对外部环境产生影响,也就是说,它不会修改任何外部状态(如全局变量、数据库、文件系统等),也不依赖于它们。函数内部的所有变化都只限于函数作用域内,不会“泄露”到函数外部。
这样的特性让纯函数在调试、测试和理解代码方面变得非常有利。它们的行为是可预测的,并且由于没有副作用,可以安全地在代码的任何地方调用它们,而不用担心会影响到程序的其他部分。
举个例子:
function add(x, y) {
return x + y;
}
这个`add`函数接受两个参数`x`和`y`,然后返回它们的和。每次给定相同的`x`和`y`值时,`add`函数都会返回相同的结果,并且它不会修改或依赖于函数外部的任何状态。因此,它是一个纯函数。
2.纯函数调用还原
因为实参固定,结果也固定,而且不依赖函数外部的变量,因此,写个插件还原此类函数的调用表达式,可以通用。
具体代码参考:
https://t.zsxq.com/18V8zIsSs
3.错误的认知
我之前以为在AST还原代码中,要求 函数调用的实参 全部为 字面量 时才可以进行计算,后来发现,使用 path.evaluate 可以获取实参的真实值。
以下面的代码为例:
var aa = 1,bb = 2;
function add(a,b)
{
return a + b;
}
var cc = add(aa,bb);
我之前的做法是先将 :
var cc = add(aa,bb);
还原成:
var cc = add(1,2);
然后再使用 2 中提到的插件进行还原。
而这里的实参aa,bb有个特点,变量定义有初始值,这些变量在其作用域内没有被改变,因此,直接使用 path.evaluate 方法可以获取它们的值:
const args = argumentsPaths.map(argPath => argPath .evaluate().value);
if (args.length == 0 || args.includes(undefined)) {
canRemoved = false;
continue;
}
let value = globalThis[id.name].apply(null, args); //计算结果
通过map返回的结果里是否包含计算结果为 undefined 的情况来获取实参值,然后进行调用计算。
4.babel库的不足
虽然 path.evaluate 方法很强大,但也有它的局限性,比如,稍微将上面的代码变形:
var aa,bb;
aa = 1;
bb = 2;
function add(a,b)
{
return a + b;
}
var cc = add(aa,bb);
这里,变量的声明和定义是分开的,再用 2 中的插件无法进行还原。
原因是 aa,bb初始化为undefined,后面又进行了重新赋值(change),因此 path.evaluate 取不到具体的实参值。
又如,在循环体中定义的变量:
function add(a, b) {
return a + b;
}
while (1) {
var aa = 1, bb = 2;
var cc = add(aa, bb);
}
使用 2 中的插件也无法进行还原,我认为这是babel库里的一个小bug:定义在循环体内的变量,不管它的值是否变过,babel库都默认 binding.constantViolations 的长度大于 0.这使得 path.evaluate 也取不到实参的真实值。
5.还原的通用思路
所以,为了保证再使用 2 中的插件尽可能多的还原函数调用表达式,我一般先将实参给还原成字面量,再作处理,就不会有问题了。
好了,今天的内容就到这里了,感谢阅读。
欢迎加入知识星球,学习更多AST和爬虫技巧。