2012-12-14 40 views
22

我沒有在應用程序中遇到內存泄漏,但我擔心未來可能出現的問題。我想知道,如果做這樣的事情:Javascript中的事件處理程序,閉包和垃圾回收

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

並讓說,這$ DIV2追加到另一個DIV這個$ DIV1。如果我叫

this.$div1.remove(); 

,後來失去了我SomeClass的實例的引用並在SomeClass的實例被垃圾回收?那麼HTML元素呢。$ div2?這個。$ div2不會在DOM裏面,因爲它被附加到這個$ div1。

我問這是因爲這個。$ div2中的事件處理程序可能會保留對HTML元素this。$ div2的引用,並且由於變量「that」而通過閉包保留對SomeClass實例的引用。

所以我應該關心如何正確移除所有事件和HTML元素?或者乾脆刪除「根」元素(這個。$ div1)解決了這個問題?

+0

我會認爲會。垃圾收集器應該跟蹤引用,所以理論上最後一個引用不見了的時候,該項目應該是垃圾收集的候選者。我不知道是否在JS引擎中的那些也可以分析不可達代碼並標記仍然有引用,但其引用未使用的項目... – RonaldBarzell

+0

這取決於。使用經典的Web應用程序,這不是一個真正的問題,因爲您經常提交給服務器,該服務器加載或刷新不同的視圖。隨着新興的單頁應用程序,這可能是一個問題,特別是像IE瀏覽器的'死'瀏覽器。 – asgoth

+0

@asgoth我正在製作一個單頁的應用程序。無需刷新頁面就必須可靠運行至少12小時,因此我擔心垃圾收集。 – Hoffmann

回答

15

this.$div2附加到this.$div1。如果我撥打this.$div1.remove();並稍後丟失我的SomeClass實例的引用,那麼SomeClass實例是否會收集垃圾?

是的,當所有對它的引用都丟失了 - 也是那些通過事件處理程序的 - 實例可以被垃圾收集。

那麼HTML元素this.$div2呢? this.$div2不在DOM內部,因爲它被附加到this.$div1

它目前是否連接到DOM並不重要。如果某些不可收集的對象引用$div1,則它也可以訪問其子節點$div2以及該事件處理程序,因此從處理程序引用的實例將不可收集。

我問這個是因爲在this.$div2事件處理程序可能會保持到HTML元素this.$div2參考,並保持到SomeClass實例的引用,通過關閉,因爲變量「那個」的。

這是一個循環引用和應該得到由引擎處理好(當沒有圈子裏的對象是從外面引用能得到收集)。但是,(舊?)互聯網探索者在圈子中涉及DOM對象時無法執行此操作。

因此,.remove jQuery methodcode)在內部調用(internal) cleanData method,它將所有事件偵聽器分開。

所以我應該關心如何正確移除所有事件和HTML元素?或者乾脆刪除「根」元素(這個。$ div1)解決了這個問題?

是的,在jQuery包裝器上調用remove會自動刪除所有事件(來自所有子元素)和DOM節點。

7

我是否應該關心如何正確刪除所有事件和HTML元素 這樣?

簡短的回答是不!至少在99%的情況下,以任何方式都無關緊要,因爲與網頁使用的整個內存相比,一個DOM元素使用的內存不重要。

但是,釋放處理不需要的對象所使用的內存總是一個好習慣,但不能說GC肯定會釋放元素使用的內存,因爲垃圾回收完全取決於瀏覽器!在理論上,GC只應該在沒有DOM元素引用的情況下使用,至少這是如何使用的,但是在JavaScript等語言中,如果沒有明確告訴運行時你完成了這個對象,那麼事情就會變得雜亂無章在JavaScript中如此之快:一個函數可能會將該對象傳遞給更多的函數,該對象可能會作爲另一個對象中的成員被保存下來,對象可能會通過閉包等被引用,所以它完全取決於瀏覽器要收集什麼!

在你的情況下刪除div1釋放HTML文檔和元素在視圖不會渲染,其實jQuery的remove方法與元素需要去除附着於該元素的所有事件,expando屬性和子元素的保健在一起本身,但是您在另一個製作DOM元素孤兒元素的對象中保留參考div1div2!除去SomeClass實例變量釋放到DOM元素的所有引用使他們的候選人進行垃圾回收,但來這裏的棘手that變量引起的DOM元素通過clusure作出的SomeClass實例做參考!這個問題被稱爲Circular Reference在IE:

JavaScript對象和存儲到一個 另一個原因Internet Explorer的垃圾收集引用不回收 內存中的DOM元素,導致內存泄漏

enter image description here

You can read more about it here

這個特殊的泄漏大部分是歷史的興趣IE < 8,但斷裂圓形鏈接的一個很好的例子是避免使用可變that,而是使用proxydelegate給事件處理程序的上下文改變到一些特定的上下文。

ECMA 5th bind method在談到DOM事件處理函數退出有用的不斷變化的環境,這裏是根據你的代碼的簡單處理程序,而無需使用可變關閉:

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

避免使用'that'變量的「提示」是無用的,因爲值仍然綁定到該函數。順便說一句,你會使用'this。$ div2.click(this.someMethod2.bind(this));',匿名函數表達式是不必要的。 – Bergi

+1

我相信你是不正確的Bergi,在上面的例子中'this'引用了DOM元素,但是沒有辦法讓DOM元素引用對象實例,知道'bind'實際上創建了一個完全** new **函數當被調用時,將其'this'關鍵字設置爲提供的值。所以基本上它是兩者之間的一個斷開的鏈接,BTW很好的捕獲,匿名函數是無用的 –

+1

我認爲DOM對實例的引用是通過回調函數?然而,我的意思是使用'bind'與使用'that'變量+函數表達式完全沒有區別。 – Bergi

1

如果您將動態創建元素,然後分配給他們的活動。我認爲你的代碼不是這樣做的好方法。如果你需要一個事件,使用這兩個函數

固定元素;:你應遵循這樣的方式第一個叫的構造,第二次在析構函數。

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

動態對象。在創建元素時添加事件,並在銷燬元素之前刪除這些事件。