爲什麼下面的代碼泄漏?爲什麼在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.
}
注:addEvent
和removeEvent
只是到Internet Explorer和其他瀏覽器之間的抽象attachEvent
/addEventListener
差異。
我創建了一個jsFiddle項目,它演示了這個問題。只需啓動Internet Explorer 8並在任務管理器或Process Explorer中觀看即可。此外,您將在那裏看到addEvent
和removeEvent
的定義。
編輯:嗯,我想出了以下解決方案。它不漂亮,但它的工作原理! 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.
我知道IE曾經有一個bug,它會使用單獨的垃圾收集來處理本地對象和javascript對象,所以無論何時你在JavaScript對象和本地對象之間形成一個循環,垃圾收集器都不能清理它。這可能嗎?我在想這個item指向item.elem,它是一個具有處理程序引用回項目的div。 – btilly 2011-02-04 22:13:45
- 與你的問題並不真正相關,但你可以通過使用`listener.apply(item,arguments)`來消除`self`變量。 :-) – 2011-02-04 22:15:02