6

たとえば、スムーズなアニメーションが重要なブラウザー アプリを作成しているとします。ガベージ コレクションが実行を長時間ブロックしてフリーズを引き起こす可能性があることはわかっているため、作成するガベージの量を最小限に抑える必要があります。ガベージを最小限に抑えるには、メインのアニメーション ループの実行中にメモリの割り当てを避ける必要があります。

しかし、その実行パスにはループが散らばっています。

var i = things.length; while (i--) { /* stuff */ }

for (var i = 0, len = things.length; i < len; i++) { /* stuff */ }

そして、それらのvar文のallocate memoryは、ガベージ コレクターが削除する可能性のあるメモリを割り当てる可能性がありますが、これは避けたいことです。

では、JavaScript でループ構造を記述して、それぞれにメモリを割り当てるのを避けるには、どのような戦略がよいのでしょうか? 長所と短所をリストした一般的な解決策を探しています。


私が思いついたアイデアは以下の3つです。

1.)インデックスと長さの「トップレベル」変数を宣言します。どこでも再利用

app.i先頭にandを宣言app.lengthして、何度も再利用できます。

app.i = things.length; while (app.i--) { /* stuff */ }

for (app.i = 0; app.i < app.length; app.i++) { /* stuff */ }

長所:実装が簡単です。短所:プロパティの逆参照によるパフォーマンスの低下は、ピュロスの勝利を意味する可能性があります。誤ってプロパティを誤用/破壊し、バグを引き起こす可能性があります。

2.)配列の長さがわかっている場合は、ループしないでください -- 展開します

配列に特定の数の要素があることが保証される場合があります。事前に長さがわかっている場合は、プログラムでループを手動で巻き戻すことができます。

doSomethingWithThing(things[0]);
doSomethingWithThing(things[1]);
doSomethingWithThing(things[2]);

長所:効率的。短所:実際にはめったに可能ではありません。醜い?変わるの面倒?

3.)ファクトリ パターンを使用してクロージャーを活用する

コレクションの要素に対してアクションを実行する関数である「ルーパー」を返すファクトリ関数を作成します ( a la _.each)。ルーパーは、作成されたクロージャー内のインデックス変数と長さ変数へのプライベート参照を保持します。ルーパーは呼び出されるたびにリセットする必要がiあります。length

function buildLooper() {
  var i, length;
  return function(collection, functionToPerformOnEach) { /* implement me */ };
}
app.each = buildLooper();
app.each(things, doSomethingWithThing);

長所:より機能的で、より慣用的ですか? 短所:関数呼び出しはオーバーヘッドを追加します。クロージャ アクセスは、オブジェクト ルックアップよりも遅いことが示されています。

4

1 に答える 1

1

そして、それらの var ステートメントの割り当てメモリは、ガベージ コレクターが削除する可能性のあるメモリを割り当てる可能性がありますが、これは避けたいことです。

これは少し誤解されています。単に使用varすると、ヒープにメモリが割り当てられません。関数が呼び出されると、関数で使用される各変数がスタック上に事前に割り当てられます。関数の実行が完了すると、スタック フレームがポップされ、メモリがすぐに逆参照されます。

ガベージ コレクションに関連するメモリの問題が問題になるのは、オブジェクトをヒープに割り当てる場合です。これは、次のいずれかを意味します。

  • 閉鎖
  • イベントリスナー
  • 配列
  • オブジェクト

ほとんどの場合、or (または新しい ES6 戻り値のいずれか) をtypeof foo返すものはすべて、ヒープ上にオブジェクトを生成します。今は思いつかないことの方が多いかもしれません。"function""object"typeof

ヒープ上のオブジェクトに関することは、ヒープ上の他のオブジェクトを参照できることです。たとえば、次のようになります。

var x = {};
x.y = {};
delete x;

上記の例では、ブラウザは のスロットの割り当てを解除できません。これは、スロットにx含まれる値のサイズが可変であるためです。ヒープ上に存在し、そこで他のオブジェクト (この場合は のオブジェクトx.y) を指すことができます。もう 1 つの可能性は、同じオブジェクトへの 2 番目の参照があることです。

var x = {};
window.foo = x;
delete x;

ブラウザーはx、別の何かがまだそのオブジェクトを指しているため、そのオブジェクトをメモリから削除することはできません。

簡単に言えば、変数の削除について心配する必要はありません。変数は完全に機能し、完全にパフォーマンスが高いからです。ガベージ コレクションに関して言えば、ヒープ割り当ては本当の敵ですが、あちこちにいくつかの小さなヒープ割り当てがあっても、ほとんどのアプリには影響しません。

于 2014-10-25T22:09:04.967 に答える