javascript解决大量调用eval产生的内存泄漏问题

一、问题产生

本人在写一个用户可以自定义编写代码逻辑的软件。对于某个代码块,需要执行用户自己编写的javascript代码(字符串),自然而然的就用到了eval。对此我声明了一个处理类,主要逻辑函数如下。

export default class DBCCondition extends ConditionBase {
    constructor(past_results, cdata) {
        this.cdata = cdata;
    }
    //--过滤组合
    filter(result) {
        let isok =true;
        eval(this.cdata.functions.filter);
        return isok;
    }
};

在这里插入图片描述
对于this.cdata.functions.filter这个是用户自己编写的字符串代码,根据result的值,对isok属性进行修改。

//--filter的一个值如下
filter:"isok = result.getBlueSum() > 10"

实际环境中,DBCCondition类对象函数filter会被调用数以百万次。对于eval,我发现每次调用浏览器都会去分配一段内存去执行内部代码。自然而然,数以百万次的eval调用,2个G的浏览器内存会被瞬间吃光。导致进程卡死,无法进行我接下来的处理逻辑。

二、探索

网上查阅了不少关于eval的资料,找了半天,却发现并没有关于解决大量调用eval产生的内存泄漏相关的文章。我突然想到,何不将用户自定义的部分定义成函数,这样在DBCCondition 类对象初始化的时候,就将函数解出为Function?这样eval就只执行了一次。

尝试1

修改DBCCondition类:

export default class DBCCondition extends ConditionBase {
    constructor(past_results, cdata) {
        this.cdata = cdata;
        this._inner_filter = funcion(result){
			let isok =true;
            eval(this.cdata.functions.filter);
            return isok;
		}
    }
    //--过滤组合
    filter(result) {
        return this._inner_filter(result);
    }
};

写完一看才发现不行。_inner_filter内的eval还是会被执行百万次,浏览器卡死。崩溃。

尝试2

查阅eval语法,发现可以传递一个js函数,这样可以直接返回Function:

var fn = eval(function(){
	console.log("excuted");
})

fn();// 输出"excuted"

但是我不可能让用户改我的代码。用户传递过来的永远是字符串。兴冲冲的用字符串函数尝试:

var fn = eval("function(){console.log('excuted');}");

在这里插入图片描述

果然,语法都过不去。

尝试3

继续翻阅资料,发现eval函数可以在window的全局域执行。本着试试的方式,我在控制台敲出了如下代码:

var fn = eval("window.__g__= function(){console.log('excuted');}");

居然没有报错!这确实是一个可执行的js语句,并且成功进行了运行。查看fn的值:
在这里插入图片描述

ok~我成功通过字符串转换为了一个Function对象!

这下就好办了。修改DBCCondition类:

export default class DBCCondition extends ConditionBase {
    constructor(past_results, cdata) {
        this.cdata = cdata;
        this._inner_filter = eval(`window.__temp__var__=${this.cdata.functions.filter}`)
    }
    //--过滤组合
    filter(result) {
        return this._inner_filter(result);
    }
};

当然 还得修改用户自定义的字符串函数,将原本直接操作isok的逻辑改为返回,再传入result参数

//--filter的一个值如下
filter:"function(result){return result.getBlueSum() > 10}"

编译、运行。一切正常。

三、结语

eval本是不建议使用的。网上都说用eval 99.99%的情况都有其他解决方案代替。但是我这个好像就是那0.01%。如果有博友觉得我这种情况也可以不采用eval解决,欢迎评论啊~

猜你喜欢

转载自blog.csdn.net/qq_29722281/article/details/91578796