大規模な純粋なJavaScriptプロジェクトを作成する場合は回避する必要のあるブラウザー固有の問題がありますが、jQueryなどのライブラリを使用する場合は、ライブラリの設計がこれらの問題の回避に役立つと想定する必要があります。ただし、メモリリークを追跡するのはかなり難しく、特定のブラウザのバージョンごとに動作が異なる可能性があることを考慮すると、具体的にするよりも、一般的にメモリリークを回避する方法を知っている方がはるかに優れています。
- コードが何度も繰り返されている場合は、使用している変数がガベージコレクションによって破棄され、クロージャ参照に拘束されていないことを確認してください。
- コードが大きなデータ構造を処理している場合は、データを削除または無効にする方法があることを確認してください。
- コードが多くのオブジェクト、関数、イベントリスナーを構築する場合は、分解的なコードも含めるのが常に最善です。
- javascriptオブジェクトまたは関数を属性として要素に直接アタッチしないようにしてください
element.onclick = function(){}
。
- 疑わしい場合は、コードが完成したら常に片付けてください。
リークに影響を与えるのは関数の呼び出し方法だと思われているようですが、問題を引き起こす可能性のある関数の内容である可能性が常に高くなります。
上記のコードで、私の唯一の提案は次のようになります。
イベントリスナーを使用するときはいつでも、要素ごとに1つ作成するのではなく、関数を再利用する方法を見つけてください。これは、イベントの委任 (祖先/親でイベントをトラップし、反応をに委任するevent.target
)を使用するか、要素を相対的な方法で、ほとんどの場合はthis
またはに関連して処理する単一の一般関数をコーディングすることで実現できます$(this)
。
多くのイベントハンドラーを作成する必要がある場合は、通常、それらのイベントリスナーを名前付き関数として保存して、終了時に再度削除できるようにするのが最適です。これは、あなたがしているように匿名関数を使用することを避けることを意味します。ただし、それがDOMを処理するコードのみであることがわかっている場合は、使用にフォールバックして、jQueryを使用して選択した要素に適用された$(elements).unbind('click')
すべてのクリックハンドラー(匿名または非匿名)を削除できます。ただし、この後者の方法を使用する場合は、jQueryのイベント名前空間機能を使用することをお勧めします。これにより、イベントのみを削除していることがわかります。すなわち$(elements).unbind('click.my_app');
。これは明らかに、を使用してイベントをバインドする必要があることを意味します$(elements).bind('click.my_app', function(){...});
より具体的に:
匿名関数の自動呼び出し
(function(){
/*
running an anonymous function this way will never cause a memory
leak because memory leaks (at least the ones we have control over)
require a variable reference getting caught in memory with the
JavaScript runtime still believing that the variable is in use,
when it isn't - meaning that it never gets garbage collected.
This construction has nothing to reference it, and so will be
forgotten the second it has been evaluated.
*/
})();
jQueryを使用して匿名イベントリスナーを追加します。
var really_large_variable = {/*Imagine lots of data here*/};
$(element).click(function(){
/*
Whilst I will admit not having investigated to see how jQuery
handles its event listeners onunload, I doubt if it is auto-
matically unbinding them. This is because for most code they
wont cause a problem, especially if only a few are in use. For
larger projects though it is a good idea to create some beforeunload
or unload handlers that delete data and unbind any event handling.
The reason for this is not to protect against the reference of the
function itself, but to make sure the references the function keeps
alive are removed. This is all down to how JS scope works, if you
have never read up on JavaScript scope... I suggest you do so.
As an example however, this anonymous function has access to the
`really_large_variable` above - and will prevent any garbage collection
system from deleting the data contained in `really_large_variable`
even if this function or any other code never makes use of it.
When the page unloads you would hope that the browser would be able
to know to clear the memory involved, but you can't be 100% certain
it will *(especially the likes of IE6/7)* - so it is always best
to either make sure you set the contents of `really_large_variable` to null
or make sure you remove your references to your closures/event listeners.
*/
});
ティアダウンと脱構築
私は、説明に関して、ページが不要になり、ユーザーがナビゲートしているときに焦点を合わせました。ただし、上記は、今日のajaxedコンテンツと非常に動的なインターフェイスの世界でさらに関連性が高くなります。要素を絶えず作成および破棄しているGUI。
動的なJavaScriptアプリを作成している場合、コードが不要になったときに実行されるコンストラクター.tearDown
またはメソッドを持つことがどれほど重要であるかを強調することはできません。.deconstruct
これらは、大きなカスタムオブジェクト構造をステップスルーし、それらのコンテンツを無効にするだけでなく、動的に作成されて使用されなくなったイベントリスナーと要素を削除する必要があります。また、要素のコンテンツを置き換える前に、 jQueryのempty
メソッドを使用する必要があります。これは彼らの言葉でよりよく説明できます。
http://api.jquery.com/empty/
メモリリークを回避するために、jQueryは、要素自体を削除する前に、データやイベントハンドラーなどの他の構成要素を子要素から削除します。
データまたはイベントハンドラーを破棄せずに要素を削除する場合(後で再追加できるようにするため)、代わりに.detach()を使用します。
tearDownメソッドを使用したコーディングでは、より適切にコーディングする必要があるだけでなく(つまり、関連するコード、イベント、および要素の名前空間を一緒に維持するようにする)、通常、よりモジュール化された方法でコードを構築することを意味します。これは、アプリの将来性、読みやすさ、および後日プロジェクトを引き継ぐ可能性のある他の人にとって明らかにはるかに優れています。