2012-12-14 17 views
22

私のアプリケーションではまだメモリリークが発生していませんが、今後起こりうる問題について心配しています。Javascriptのイベントハンドラ、クロージャ、およびガベージコレクション

SomeClass.prototype.someMethod= function() { 
    var that= this 
    this.$div2.click(function() { 
     that.someMethod2(); 
    }); 
} 

そして、この$のDIV2が別のdivこの$のDIV1に追加されていることを言うことができます:。。私はこのような何かをやっている場合知っていただきたいと思います。私は

this.$div1.remove(); 

が呼び出して、後で私の工assのインスタンスの参照を失った場合には、ゴミ工assインスタンス集めますでしょうか。そしてHTML要素はどうですか?$ div2? $ div2はこれに追加されているのでDOM内にはありません。

この$ div2のイベントハンドラはHTML要素this $ div2への参照を保持し、変数「that」のために閉包を介してSomeClassのインスタンスへの参照を保持する可能性があるため、これを尋ねます。

このように、すべてのイベントとHTML要素を適切に削除する必要がありますか?または、単に "root"要素(これは$ div1)を削除するだけで問題を解決できますか?

+0

私はそれがそうだと思います。ガベージコレクタは参照を追跡する必要があります。したがって、最後の参照がなくなった瞬間に理論上ガベージコレクションの候補になるはずです。私は、JSエンジンのものが到達不能なコードを分析して、まだ参照を持っているが参照が未使用の項目をマークできるのだろうかと思います... – RonaldBarzell

+0

これは依存しています。古典的なWebアプリケーションでは、これは本当に問題ではありません。なぜなら、あなたは頻繁に別のビューを読み込んだり、更新したりするサーバに提出するからです。新しく登場するシングルページアプリでは、特にIEなどの「死んだ」ブラウザでは問題になる可能性があります。 – asgoth

+0

@asgoth私は1ページのアプリケーションを作っています。 1つは、ページをリフレッシュせずに少なくとも12時間確実に実行する必要があるため、ガベージコレクションに関する私の心配です。 – Hoffmann

答えて

15

this.$div2は、this.$div1に追加されます。私がthis.$div1.remove();を呼び出した後、私のSomeClassインスタンスの参照を失った場合、SomeClassインスタンスはガベージコレクションされますか?

はい、すべての参照が失われた場合(イベントハンドラを経由する場合も同様)、インスタンスはガベージコレクションされる可能性があります。

HTML要素はどうですか?this.$div2this.$div2this.$div1に追加されているため、DOM内には存在しません。

現在DOMに接続されているかどうかは関係ありません。収集できないオブジェクト参照がある場合は、子ノード$div2とそのイベントハンドラもアクセスできるため、ハンドラから参照されるインスタンスは収集できません。

this.$div2でのイベントハンドラは、HTML要素this.$div2への参照を保持しても「こと」ために、変数の閉鎖によるSomeClassのインスタンスへの参照を保持する可能性があるので、私はこれを頼みます。円形の参照だとエンジンによってうまく処理を受けるべき

(円内のオブジェクトのいずれも、それ外部から参照されていない収集得ることができます)。しかし、DOMオブジェクトがサークルに含まれている場合、(古い?)Internet Explorerはこれを実行できません。

そのため、.remove jQuery methodcode)は内部ですべてのイベントリスナーを切り離す(internal) cleanData methodを呼び出します。

このように、すべてのイベントとHTML要素を適切に削除する必要がありますか?または、単に "root"要素(これは$ div1)を削除するだけで問題を解決できますか?

はい、jQueryラッパーのremoveを呼び出すと、(すべての子要素からの)すべてのイベントとDOMノードが自動的に削除されます。

7

すべてのイベントとHTML要素を正しく削除する必要がありますか? ?

短い答えはいいえです!少なくとも99%のケースでは、1つのDOM要素で使用されるメモリがWebページで使用される全体的なメモリに比べて些細なので、何の意味もありません。

不要なオブジェクトを破棄することによって使用されるメモリを解放することは常に推奨されますが、ガベージコレクションは完全にブラウザに依存するため、GCが要素によって利用されるメモリを確実に解放するとは言えません。理論的には、DOM要素への参照がなくても、少なくともそれはどのようにしてChrome worksですが、JavaScriptなどの言語では、オブジェクトで完了した実行時間を明示していないと、 JavaScriptはすばやく:関数がオブジェクトをいくつかの関数に渡すことがあります。オブジェクトは別のオブジェクト内のメンバとして保存され、オブジェクトはクロージャなどを介して参照される可能性があります。何を集めるか! div1を取り除く、あなたの場合は

はjQueryのremove方法は、素子と共に要素に接続されたすべてのイベント、Expandoでプロパティ、子要素を削除するの面倒を実際には、HTML文書やビューでレンダリングされませになる要素を、解放しますしかし、DOM要素の両方を作成するさらに別のオブジェクトには、div1div2の参照を保持します。孤児要素! SomeClassインスタンス変数を削除すると、DOM要素への参照がすべてガベージコレクションの候補になりますが、DOM要素がSomeClassの参照をclusure経由で参照する原因となるトリッキーなthat変数が発生します。メモリリークに

enter image description here

をその結果、 メモリを再利用しないようにするJavaScriptオブジェクトと1つの 別の原因Internet Explorerのガベージコレクタへの参照を格納DOM要素

:この問題は、IEでCircular Referenceとして知られています

You can read more about it here

この特定のリークは、ほとんどが歴史的ですinterest IE < 8ですが、循環リンクを切断する良い例は、変数thatの使用を避け、proxyまたはdelegateを使用して、イベントハンドラのコンテキストを特定のコンテキストに変更することです。

this.$div2.click((function() { 
     this.someMethod2(); 
    }).bind(this)); 
+0

値がまだ関数にバインドされているので、 'that'変数を避けるためのあなたの"ヒント "は役に立たない。 Btw、 'this。$ div2.click(this.someMethod2.bind(this));'を使うと、無名関数式は不要です。 – Bergi

+1

私はあなたが間違っていると信じています上記の例では 'this'がDOM要素を参照していますが、DOM要素がオブジェクトインスタンスを参照する方法はありませんが、' bind'が実際に完全** new **関数は、呼び出されると、 'this'キーワードが指定された値に設定されます。だから基本的にそれは2つの間の壊れたリンクです、BTWいいキャッチ、無名関数は全く役に立たないです。 –

+1

DOMからインスタンスへの参照は、そのコールバック関数を介していたと思った?しかし、私が意図したことは、 'bind'を使うことは、' that'変数+関数式を使うこととまったく変わらないということでした。 – Bergi

1

あなたが動的に要素を作成する場合は、それらにイベントを割り当てる:

ECMA 5th bind methodはでDOMイベントハンドラに来るとき、ここにあなたのコードに基づいて、単純なハンドラが変数クロージャを使用せずにだ有益な変化コンテキストを終了しています。私はあなたのコードはそれを行う良い方法ではないと思います。次のようにしてください:

固定要素の場合、イベントが必要な場合は、これらの2つの機能を使用してください。コンストラクタで最初に呼び出され、デストラクタで2番目に呼び出されます。

on_Events: function() { 
    $('your_form').on('event_name', {element_Selector}, callback_function) 
}, 
off_Events: function() { 
    $('your_form').off('event_name', {element_Selector}, callback_function) 
} 

動的オブジェクトです。要素を作成するときにイベントを追加し、要素を破棄する直前にこれらのイベントを削除します。

関連する問題