JavaScriptはevalへの多数の呼び出しによって引き起こされるメモリリークの問題を解決します

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を使わなくてもこの状況を解決できると思ったら、コメントしてください〜

おすすめ

転載: blog.csdn.net/qq_29722281/article/details/91578796