1.問題が発生する
ユーザーがコードロジックをカスタマイズできるソフトウェアを書いています。あるコードブロックについては、ユーザーが記述した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
、これはユーザによって書かれた文字列のコードが、isok
変更属性を結果の値に応じ。
//--filter的一个值如下
filter:"isok = result.getBlueSum() > 10"
実際の環境では、DBCCondition
クラスオブジェクト関数filter
は何百万回も呼び出されます。evalの場合、ブラウザーが呼び出されるたびに、内部コードを実行するためにメモリのセクションが割り当てられることがわかりました。当然のことながら、何百万ものeval呼び出しがあると、2 Gのブラウザメモリが瞬時に消費されます。プロセスをフリーズさせ、次の処理ロジックを実行できませんでした。
2.探索
私はインターネットで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は引き続き100万回実行され、ブラウザはフリーズします。崩壊。
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の値を表示します。
はい、文字列を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}"
コンパイルして実行します。すべてが正常です。
3.まとめ
evalは推奨されません。インターネット上では、99.99%のケースをevalに置き換える他のソリューションがあると言われています。しかし私のものは0.01%のようです。ブロガーがevalを使わなくてもこの状況を解決できると思ったら、コメントしてください〜