Why does this leak in Internet Explorer 8?

喜夏-厌秋 提交于 2020-01-02 01:01:29

问题


Why does the following code leak?

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.
}

Note: addEvent and removeEvent are just to abstract attachEvent/addEventListener differences between Internet Explorer and other browsers.

I created a jsFiddle project which demonstrates the problem. Just fire up Internet Explorer 8 and watch it go in Task Manager or Process Explorer. Also, you will see the definition of addEvent and removeEvent there.

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

EDIT: Well, I came up with the following solution. It is not pretty, but it works! 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.

回答1:


The problem is the events (as almost always in Internet Explorer, BTW).

Look at http://jsfiddle.net/rJ8x5/39/ and notice how it garbage collects fine.

You are creating circular references when you attach the events. Read more about it in Circular references to DOM objects on an HTML page cause a memory leak.




回答2:


The code leaks because you are calling attachEvent improperly - Microsoft's documentation insists that the event name be a standard DHTML event.

If you change 'eventName' to 'click', it does not leak.

A more robust solution would be to change your event attaching code to check 'on'+eventname in domElement and refuse to attach the event if this is false.



来源:https://stackoverflow.com/questions/4903379/why-does-this-leak-in-internet-explorer-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!