最近書いた記事、ES 6-弱いマップをざっと見てdata()
、jQueryがリークをなくす方法を説明しました。基本的に、expandoプロパティ名を生成しますjQuery.expando
。データを要素にアタッチすると、データは内部キャッシュ配列にプッシュされ、要素には、キャッシュ内のデータのインデックスの値を持つexpandoプロパティが与えられます。これに似たもの:
element[jQuery.expando] = elementId;
循環参照を防ぐ方法は、オブジェクトをエキスパンドとして要素に直接アタッチしないことです。要素への参照がコードに残っている場合、その要素がDOMから削除されても、その要素をガベージコレクションすることはできません。ただし、循環参照を防止してもリークが完全に塞がれるわけではありません。要素がDOMから削除され、ガベージコレクションが行われた場合でも、配列にはデータが残ります。したがって、jQueryはページのアンロード時に配列をクリアし、要素が.のような独自のメソッドを使用してDOMから削除された場合は、配列からデータを削除しますremove()
。のためにデータを存続させdetach()
ます。
jQueryがこれを行う理由は、同等の弱いマップがないためです。これは、ES 5では一種のシマブルですが、ES 3ではそうではありません。私の記事で説明したように、WeakMap
まさにこの種の状況のために作られていますが、利用可能な実装はFirefox 6以降、および仕様が確定していないため、本番環境では使用しないでください。
私の記事から得られるもう1つの点は、特定の要素ではexpandoプロパティをアタッチできないことです<object>
。<embed>
これは、jQueryソースコードで名前が付けられて恥をかかされている2人の犯人です。これらの要素の場合、あなたはかなり困惑していて、jQueryはdata
それらを使用することを許可しません。
基本的な循環参照メモリリークは、2つのオブジェクトのプロパティが相互に直接参照を保持している場合に、参照カウントされた実装で発生します。したがって、 DOMObjectはJSObjectへの参照を保持し、その逆も同様です。どちらのオブジェクトへの参照も他にないと仮定すると、両方のオブジェクトの永続的な参照カウントは1であり、GCはそれらを収集対象としてマークしません。
古いブラウザ(IE6)は、ページのアンロードでもこれらの循環参照を壊しませんが、新しいブラウザは、それらの原因となるパターンを認識することで、これらの循環参照の多くを壊すことができます。 DOMObjectがJSObjectへの参照を保持することはないjQuery.cache
ため、同様のパターンはメモリリークを部分的に無効にします。したがって、JSObjectがDOMObjectへの参照を保持している場合でも、GCはJSObjectへの参照がなくなるとコレクション用にJSObjectをマークできます。GCがJSObjectを収集すると、 DOMObjectの参照数が減り、収集のために解放されます。
IE 8+およびその他の参照カウントブラウザーは、多くの循環参照パターンを破ることができる場合がありますが(IE 8では約400が修正されました)、リークの可能性は減少するだけです。たとえば、スクリプト要素とJSONPを操作しているときに、IE8の自分のアプリの1つで大きなリークが発生しました。最善の解決策は、最悪の事態を計画するWeakMap()
ことです。それがなければ、jQueryデータパターンを使用するのが最善の方法です。確かに、孤立したオブジェクトを持つリスクがあるかもしれませんが、これは2つの悪のうちの小さい方です。