2011-06-13 61 views
6

對於我來說,有人會爲了防止內存泄漏而爲我分配一個空值修復的性質嗎?Javascript內存泄漏:爲什麼要將對象分配給空工作?

我們都熟悉以下技術來阻止DOM對象和JS對象之間的循環引用,以防止內存泄漏:

function foo() { 
     var ele = document.getElementById("someParagraphId"); 

     ele.onclick = function() { 
     //some action here 
     }; 

     ele = null; 
    } 

的問題是,爲什麼會有上述工作?將「ele」設置爲null將肯定會停止循環引用,但不會阻止將來引用「ele」嗎?

function foo() { 
     var ele = document.getElementById("someParagraphId"); 

      ele.onclick = function() { 
       console.log("accessing ele ... after set to null " + ele.onclick); 
      }; 

     ele = null; 

    } 

然而,事件偵聽器觸發。它會抱怨「ele」對象爲null(這是我們所期望的)。

鑑於上述行爲,我們是否有理由推斷出JavaScript引擎實現將持有對事件偵聽器的某種內部引用,並且它是在引發事件時調用的此引用?

eventHandlerRef = //assignment to "ele.onclick" handler/listener; 

如果有像上面這樣的引用,那麼賦值到零修復是不是依賴於實現?或者,它是ECMAScript規範的一部分。

從我的理解,這個修復一直是跨瀏覽器的安全。我沒有遇到過很多例子,在應用null賦值之前,我們會特別提到檢測/嗅探瀏覽器的類型。

===============編輯==================

我認爲由於我提出這個問題的方式可能無意中直接從我想傳達的內容中進行討論。正在引用的幾個概念:

對象句柄/對象引用ALA:

var obj1, obj2; 
    obj1 = {foo: "bar"}; //obj1 points to the an area of the heap allocated 
          //for the object literal. 

obj2 = obj1;  //obj2 points to the same position in the heap 
         //as obj1. 

//obj1 = null or 
obj1 = {/*another obj literal*/} //the new object literal is created 
             //and allocated in some other part 
             //in the heap, and obj1 now points 
             //to the new location. 

//obj2 is unaffected by obj1's re-assignment. 

以上是不是在我的癢癢的謊言,我就後悔加入一行:

console.log("accessing ele ... after set to null " + ele.onclick); 

以上使得這看起來是一個閉包問題。我完全預料到原始帖子中顯示的錯誤會被拋出。

我的癢更多地是出於某種原因......在我看來,我一直認爲Javascript引擎會在事件觸發並將元素設置爲null時直接調用ele.onlick()類似於以下流程:

var obj = {}; 
obj.method = function() {}; 

obj = null; 
//error calling obj.method(), i.e., obj is null 

鑑於我們知道在原崗位後ELE已設置爲空的事件處理程序仍是起火,在我的腦海裏流更像是:

var obj = {}; 
obj.method = function() {}; 

var objRef = obj; 

obj = null; 

//The Javascript Engine calling objRef.method when event triggered. 

我的癢來回到問題,上面是如何最Ja vascript的實現工作,其中一些內部引用指向分配的事件處理程序,並且在事件觸發時調用此內部引用?直接(暫時擱置設計和架構問題),什麼是阻止JavaScript引擎實現從調用ele.onclick()?或者措詞不同,

也許我的思考過程的工作方式不同,但是當別人第一次遇到賦值爲null的修復時,沒有其他人再次看到它,其中元素引用是空的,但代碼爲處理程序仍然被執行?

+0

我編輯了我的答案以解決您的編輯問題。 – zneak 2011-06-13 14:43:29

回答

0

由於您首先通過調用document.getElementById("someParagraphId")獲得ele,則在您分配ele之前,它引用的對象已存在於內存中。它是文檔的一部分 - 當然,除了ele以外,其他內存中都有對它的引用。

6

都乾淨利索老答案,解決編輯:)

讓我們更簡單的例子:textContent

var ele = document.getElementById('foo'); 
ele.textContent = 'abc'; 
ele = null; 

這個代碼設置的#footextContentabc,並丟棄該參考ele。現在,如果我問#foo一遍嗎?發生了什麼......

var ele = document.getElementById('foo'); 
ele.textContent = 'abc'; 
ele = null; 
ele = document.getElementById('foo'); 
console.log('#foo is ' + ele.textContent); 

它將記錄"#foo is abc"。這只是表明#foo生活在我的腳本之外,並且document保留對它的引用(因爲我已經回撥了document上的一個方法)。它與事件處理程序的工作方式相同。

var ele = document.getElementById('foo'); 
ele.onclick = function() { console.log('abc'); }; 
ele = null; 
ele = document.getElementById('foo'); 
console.log('#foo.onclick is ' + ele.onclick); 

事件處理程序不是一種特殊的屬性。它們只是你寫參考(函數)的變量。這些大致是功能性語言功能,其中功能可以用作簡單值。 JavaScript實現只是從DOM引用調用事件處理程序,而不是從ele引用中調用事件處理程序。讓我們做一個更簡單的例子,沒有document.getElementById。如你所見,函數並不與我們賦值的引用綁定在一起:它們綁定到引用指向的對象。您可以從對該對象的任何引用中調用它們,而不僅僅是您用來分配函數的那個​​對象。因此,您可以取消引用以打破循環依賴關係,而不會影響對象的正確行爲。

+0

我認爲你可以更簡單地解釋閉包。閉包由一個內部函數形成,該內部函數可以訪問外部函數的變量和參數。外部函數的變量對象(其所有局部變量和參數作爲屬性)位於內部函數的作用域鏈上。標識符解析遵循檢查局部變量對象的典型模式,然後是範圍鏈上的下一個(閉包),然後是下一個等,直到達到全局對象。 – RobG 2011-06-13 05:30:03

0

我離一個JavaScript專家很遠;但我想我至少可以部分回答你的問題,只需提供一個變量對象之間的區別。讓我們一次一行地查看您的代碼。

var ele = document.getElementById("someParagraphId"); 

在這裏,我們正在做兩件事情:

  • 聲明一個可變ele
  • 分配到DOM元素的引用(一個對象)給該變量

下一個:

ele.onclick = function() { 
    console.log("accessing ele ... after set to null " + ele.onclick); 
}; 

在這裏,我們使用的是可變ele一個回調分配給onclick事件對象(再次,DOM元素),其它引用的。

最後:

ele = null; 

最後我們分配一個null參考我們的ele變量。它不再引用剛纔的對象。 但是,該對象仍然存在,使用相同的onclick處理程序;它沒有改變。

處理程序仍然被調用的事實是正確的行爲。現在,對於錯誤,讓我們再看看在分配給DOM元素的onclick事件功能:

console.log("accessing ele ... after set to null " + ele.onclick); 

ele是非常相同的變量我們剛分配到null。因此,當這個函數被調用時 - 因爲再次,連接到對象onclick處理程序還沒有消失 - 拋出空引用異常。