1. The problem arises
I am writing a software where users can customize the code logic. For a certain code block, the javascript code (string) written by the user needs to be executed, and eval is naturally used. For this I declare a processing class, the main logic functions are as follows.
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;
}
};
For this.cdata.functions.filter
this is a string code written by the user, isok
modify the attribute according to the value of result .
//--filter的一个值如下
filter:"isok = result.getBlueSum() > 10"
In the actual environment, the DBCCondition
class object function filter
will be called millions of times. For eval, I found that every time the browser is called, a section of memory is allocated to execute internal code. Naturally, with millions of eval calls, 2 G of browser memory will be consumed instantly. Caused the process to freeze, unable to carry out my next processing logic.
2. Explore
I checked a lot of information about eval on the Internet and searched for a long time, but found that there is no article about solving the memory leak caused by a large number of calls to eval. It suddenly occurred to me, why not define the user-defined part as a function, so that when the DBCCondition
class object is initialized, the function is solved Function
? In this way, eval is executed only once.
Try 1
Modify the DBCCondition
class:
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);
}
};
After reading it, I found out that it didn't work. _inner_filter
The eval inside will still be executed a million times, and the browser will freeze. collapse.
Try 2
Check the eval grammar and find that you can pass a js function so that it can be returned directly Function
:
var fn = eval(function(){
console.log("excuted");
})
fn();// 输出"excuted"
But I can't let users change my code. What the user passes is always a string. I am excited to try with string functions:
var fn = eval("function(){console.log('excuted');}");
Sure enough, the grammar couldn't make it through.
Try 3
Continue reading the information and found that the eval function can be window
executed in the global domain. In the way of trying, I typed out the following code in the console:
var fn = eval("window.__g__= function(){console.log('excuted');}");
No error was reported! This is indeed an executable js statement, and it runs successfully. View the value of fn:
ok~ I successfully converted a string into an Function
object!
It's easy now. Modify the DBCCondition
class:
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);
}
};
Of course, you have to modify the user-defined string function, change the original direct operation isok
logic to return, and then pass in the result
parameters
//--filter的一个值如下
filter:"function(result){return result.getBlueSum() > 10}"
Compile and run. everything is normal.
3. Conclusion
eval is not recommended. It is said on the Internet that there are other solutions to replace 99.99% of the cases with eval. But mine seems to be 0.01%. If any blogger thinks I can solve this situation without using eval, please comment~