2

次のような繰り返しアクティブ化されるコールバックがある場合...

Template.foo.rendered = function() {
    $(this.firstNode).droppable({
        // other arguments
        drop: function() {
            // some really long function that doesn't access anything in the closure
        }
    });
}

次のように最適化する必要がありますか?

dropFunction = function() {
    // some really long function that doesn't access anything in the closure
} 

Template.foo.rendered = function() {
    $(this.firstNode).droppable({
        // other arguments
        drop: dropFunction
    });
}

この場合、renderedコールバックは Meteor コンストラクトであり、DOM ノードfooが構築されるたびにテンプレートを使用して非同期に実行されます。それらのかなりの数がある可能性があります。グローバル クロージャのどこかで関数を宣言することは、Javascript エンジンが追加のローカル クロージャを追跡する手間を省くために役立ちますか?それとも問題ではありませんか?

4

2 に答える 2

1

私は、最新のブラウザーでは違いさえあるとは信じていません。クロージャー オブジェクトを作成するとき、V8 は関数が使用する変数を決定し、変数を使用しない場合はクロージャー オブジェクトに入れません (しばらく前にオンラインでこれを読みましたが、申し訳ありませんが見つかりませんリンク*以下を参照*)。JavaScript をコンパイルするブラウザーはどれも同じことをすると思います。

最適化されるのは関数の作成です。2 番目の例では、レンダリングされた関数と同時にドロップ関数が作成されます。最初の例では、レンダリングが呼び出されるたびに再作成されます。これが長い for ループにあった場合、最適化する価値があるかもしれません。

個人的には、最初の例の方が読みやすく維持しやすいと思います。そして、質問に対するコメントで述べたように、あなた (または将来このコードを編集する人) が誤って関数変数 (イベント ハンドラーなど) を上書きする可能性があり、手にバグを見つけるのが難しい可能性があります。番号 1 で言うか、すべてを自己実行関数にラップして、dropFunction のスコープが小さくなり、上書きされる可能性が低くなるようにすることをお勧めします。

(function () {
    var dropFunction = function() {
        // some really long function that doesn't access anything in the closure
    } 

    Template.foo.rendered = function() {
        $(this.firstNode).droppable({
            // other arguments
            drop: dropFunction
        });
    }
})();

編集:リンクを見つけました。実際には、Meteor のクロージャ メモリ リークに関するブログ投稿にありました!

http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html

これが私が言及した部分です:

JavaScript の実装 (または少なくとも現在の Chrome) は、str が logIt で使用されていないことに気付くほどスマートであるため、logIt のレキシカル環境には配置されず、実行が終了したら大きな文字列を GC しても問題ありません。

于 2013-09-26T18:41:15.837 に答える
0

私は流星について何も知らないと言ってこれを始めます:/しかし、何が正しく起こるかを理解していれば、基本的に:

Template.foo.rendered

繰り返し呼び出されます。それが発生すると、最終的に呼び出される「ドロップ」上に新しい関数を作成してオブジェクト + 定義します。その場合は、パフォーマンスの観点から、解析時に 1 回ドロップを定義することをお勧めします。ただし、そのメリットはおそらく、 render が呼び出される頻度によって異なります。

理由については、ここに似たようなものを投稿しました:

ループ内の JavaScript での関数宣言と関数式のパフォーマンス

そして、@soktinpkの答えは素晴らしい要約だと思います。

上記のリンクのjsperfを見ると、最初は直接適用できないように見えるかもしれませんが、上記の想定されたステートメントが真実である場合、実際にはそうです-手元の問題は、実行時に関数を定義するのにどれだけ費用がかかるかということです。時間を解析し、実行時にこの関数を何度も定義していますか。

関数が実行時に定義されるループと、関数が解析時に定義される (または解析時に定義されてホイストされる) ループのパフォーマンスを見ると、解析時のループの方が大幅に高速に実行されます。

あなたの例で meteor が行っていることと同等のものは、おそらく次の jsperf のようなものだと思います: http://jsperf.com/how-expensive-is-defining-a-function-at-runtime2

ここで行ったことは、シナリオが何度も呼び出されることをシミュレートする 2 つのループを作成することです。1 つ目は、オブジェクトと関数を作成する関数を呼び出し、それを呼び出しのために別の関数に渡します。

2 つ目は、関数を参照するオブジェクトを作成し、それを呼び出しのために別の関数に渡します。パフォーマンスの違いはかなり大きいように見えます (クロムで 58%)。

于 2014-11-07T14:57:05.997 に答える