¿Por qué se filtra el siguiente código?¿Por qué se produce esta fuga en 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.
}
Nota: addEvent
y removeEvent
son sólo para abstraer attachEvent
/addEventListener
diferencias entre Internet Explorer y otros navegadores.
Creé un proyecto jsFiddle que demuestra el problema. Simplemente inicie Internet Explorer 8 y mire cómo va en Task Manager o Process Explorer. Además, verá la definición de addEvent
y removeEvent
allí.
EDIT: Bueno, se me ocurrió con la siguiente solución. No es bonito, pero funciona! 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.
Sé que IE solía tener un error en el que utilizaría recolección de elementos no utilizados para objetos nativos y objetos JavaScript, por lo que cada vez que se forma un ciclo entre objetos JavaScript y objetos nativos, ni el recolector de basura podría limpiarlo. ¿Podría ser eso? Estoy pensando que el elemento apunta a item.elem que es un div que tiene manejadores que se refieren al artículo. – btilly
- No es realmente pertinente a su pregunta, pero puede eliminar la variable 'self' utilizando en su lugar 'listener.apply (item, arguments)'. :-) –
¡Totalmente! Pero esta es una versión muy simplificada de una clase que escribí donde el método addEvent no tiene acceso a la instancia tan convenientemente - por lo tanto, var self = this – jordancpaul