2011-02-04 106 views
11

爲什麼下面的代碼泄漏?爲什麼在Internet Explorer 8中泄漏?

for (var i = 0; i < 100; i++) { 
    var item = {}; 
    item.elem = document.createElement('div'); 
    document.body.appendChild(item.elem); 
    item.addEvent = function(name,listener) { 
     var self = this; 
     var wrappedListener = function() { 
      return listener.apply(self,arguments); 
     } 
     //Uh-oh creating a circular reference here! 
     //The wrappedListener has a closure on self and therefore on item.elem. 
     addEvent(this.elem,name,wrappedListener); 
     return wrappedListener; 
    } 
    var wrap = item.addEvent('eventName',listen); 

    //Now remove the eventHandler - this should free up the circular reference. 
    removeEvent(item.elem, 'eventName', wrap); 
    if (item.elem.parentNode) { 
     item.elem.parentNode.removeChild(item.elem); 
    } 
    //item.elem = null; //With this also un-commented, the leak disappears. 
    //The fact that I have to null item.elem tells me that something is holding 
    //a reference to item, and therefore elem. Setting elem to null fixes the 
    //problem, but since I am removing the event handler, I don't think this 
    //should be required. 
} 

注:addEventremoveEvent只是到Internet Explorer和其他瀏覽器之間的抽象attachEvent/addEventListener差異。

我創建了一個jsFiddle項目,它演示了這個問題。只需啓動Internet Explorer 8並在任務管理器或Process Explorer中觀看即可。此外,您將在那裏看到addEventremoveEvent的定義。

http://jsfiddle.net/rJ8x5/34/

編輯:嗯,我想出了以下解決方案。它不漂亮,但它的工作原理! http://jsfiddle.net/rJ8x5/43/

var item = {}; 
item.elem = document.createElement('div'); 
document.body.appendChild(item.elem); 
item.addEvent = function(name,listener) { 
    var wrappedListener = function() { 
     //Access the scope through the callee properties. 
     return listener.apply(arguments.callee.scope, arguments); 
    } 
    addEvent(this.elem,name,wrappedListener); 
    //Save the scope not as a closure, but as a property on the handler. 
    wrappedListener.scope = this 
    return wrappedListener; 
} 
var wrap = item.addEvent('eventName',listen); 
removeEvent(item.elem, 'eventName', wrap); 
//Force the circular reference to GO AWAY. 
wrap.scope = null 
if (item.elem.parentNode) { 
    item.elem.parentNode.removeChild(item.elem); 
} 
//item.elem = null; //No longer needed. 
+0

我知道IE曾經有一個bug,它會使用單獨的垃圾收集來處理本地對象和javascript對象,所以無論何時你在JavaScript對象和本地對象之間形成一個循環,垃圾收集器都不能清理它。這可能嗎?我在想這個item指向item.elem,它是一個具有處理程序引用回項目的div。 – btilly 2011-02-04 22:13:45

+0

- 與你的問題並不真正相關,但你可以通過使用`listener.apply(item,arguments)`來消除`self`變量。 :-) – 2011-02-04 22:15:02

回答

1

由於您不正確地調用attachEvent,代碼泄漏 - 微軟的documentation堅持認爲事件名稱是標準的DHTML事件。

如果將'eventName'更改爲'click',則不會泄漏。

更強大的解決方案是更改您的事件附加代碼,以檢查'on'+eventname in domElement並拒絕附加事件,如果這是錯誤的。