2011-04-18 57 views
2

在最近的採訪中,有人問我:「像這段代碼一樣,在引用DOM元素時使用閉包有什麼危險?」從閉包中引用DOM元素引發IE內存泄漏?

var firstNameValue = (function(elementId) { 
    var firstName = document.getElementById(elementId); 
    return firstName.value; 
})("firstName"); 

顯然,我不知道,上面的代碼在IE中創建了內存泄漏。給定的基本原理非常模糊,我不明白,但顯然這可能只適用於較舊的IE版本?

任何人都可以詳細說明這一點?

+1

的提問要麼是非常愚蠢的或很聰明。啞,因爲沒有關閉或內存泄漏,整個事情相當於:'var firstNameValue = document.getElementById('firtName')。value;'。或者非常聰明,因爲提及關閉和內存泄漏是對真正問題的干擾 - 代碼的不必要的複雜性。 – RobG 2011-04-19 23:01:27

+0

據報道,IE8中「內存泄漏問題」已得到解決。不過,正如@RobG在他的回答中所述,我不明白這是如何在舊版瀏覽器中造成內存泄漏的。下面的MSDN頁面列出了一系列可能導致IE7及更早版本內存泄漏的測試用例 - 我沒有看到這個模式。 http://msdn.microsoft.com/en-us/library/dd361842(VS.85).aspx – MrWhite 2012-11-26 18:34:18

回答

5

用於處理由DOM分配的內存的IE的內存中使用的垃圾收集器不知道如何釋放可能由JScript引擎分配的內存。因此,它只是忽略了這樣的事情。

因此,你將一個事件處理程序綁定到一個DOM元素(或類似的東西),而你的事件處理程序是一個函數在一些其他函數的調用中創建的,而另一個函數有一個本地數組那麼,在DOM元素本身被廢棄之後很久,這些億萬的東西就會生存下來,並且甚至在包含它的很久之後它已經被釋放(我想;這已經有一段時間了)。

function bindHandler(domElement) { 
    var hoHumWhatever = generateGiganticObjectNow(); 

    domElement.onclick = function() { 
    alert("oww you clicked me"); 
    }; 
} 

既然在關閉中保留了「hoHumWhatever」變量。當頁面重新加載或修改了DOM以便元素被拋棄時,DOM垃圾回收器將無法對指向JScript擁有的內存的屬性執行任何操作。另一方面,JScript不知道DOM節點已經被釋放,所以它認爲閉包內存仍然被引用。

我承認這在某些細節上可能不準確,但這是基本問題。不同的人已經寫了這個,包括克羅克福德先生和(我認爲)ppk在quirksmode。

編輯在更仔細的閱讀您發佈的代碼,我認爲可能是類似但相反的情況下的一個例子:小函數返回一個DOM價值的一部分的引用,所以也許有人說JScript會掛在DOM內存上(而不是反之亦然)。現在,在這個特例中,我有點懷疑,因爲我沒有看到除了對DOM屬性的簡單引用(該屬性應該是一個原始字符串實例並且真的不應該「不會造成問題。這些事情可能是騙人的,所以我只是坐在這裏,抓我的頭。

+0

談論「堆棧溢出」:) – 2011-04-18 21:13:34

+0

我同意你,我需要看到它證明了上述造成泄漏。 **不存在閉包**,「小函數」(稱爲一個iife)不會持久存在,即使存在,它也不會在其外部範圍引用任何變量(儘管它的確引用了DOM元件)。唯一可能的泄漏來自DOM參考,如果這樣會造成泄漏,那麼您究竟如何避免泄漏? IE內存泄漏的主要問題是涉及DOM元素的循環引用(根據您的示例),但是已經修復(據我所知)。 – RobG 2011-04-19 00:59:04

4

以下代碼不會創建任何閉包(見下文)或內存泄漏。

var firstNameValue = (function(elementId) { 
    var firstName = document.getElementById(elementId); 
    return firstName.value; 
})("firstName"); 

與IE 6調查的比特表明該代碼不創建內存泄漏。我加了1000點的div的Lorem存有和唯一ID大板,然後跑1000個匿名函數每上面的代碼我每次刷新使用它了,我打開頁面之前它固執地回到了內存的頁面。即使在頁面中添加數千個元素以超過100MB也沒有讓它變得渺茫,IE仍然恢復到原來的大小。

因此,無論這是一個有趣的問題(即正確答案是「沒有永久關閉,沒有循環引用,所以不存在內存泄漏」)或任何人認爲它沒有正確地寫出來。

如果它不是一個詭計的問題,看看你是否可以得到誰寫的或者問了這個問題給你一個實際的演示。

封閉

一個封蓋的簡單解釋是 該ECMAScript的允許內 功能;函數定義和 函數表達式是其他函數的函數表達式。 並且那些內部函數是 允許訪問所有本地 變量,參數和在其外部函數 中聲明 函數。當的那些內部函數 一個由在它被包含 訪問的功能的外側形成的封閉件,使得它可以 外部函數 返回之後被執行。在這一點上它仍然可以訪問局部變量 參數和內部函數 的外部函數聲明。 這些局部變量,參數和 函數聲明(最初)具有 它們在 外函數返回時具有的值,並且 可能與內函數進行交互。

理查德康福德等人, 「的Javascript瓶蓋http://www.jibbering.com/faq/notes/closures/