2010-02-23 16 views
18

estoy usando jQuery y hacer algo como estoJavascript a pérdidas de memoria

DOM:

<div id="parent"></div> 

JS:

var _doSomeThing = function() 
{ 
    //some codes 
} 

$(function() 
{ 
    // appending div and binding methods to span 
    $('#parent').append('<span>1</span>'); 
    $('#parent').append('<span>2</span>'); 
    $('#parent span').bind('click', _doSomeThing); 
}); 

function _clearDiv() 
{ 
    //clear div 
    $('#parent').html(''); 
} 

//sometime in future, call clear div 
_clearDiv(); 

Ahora mi pregunta es, hacer eventos de unión a DOM y más tarde sólo quitar los elementos de DOM conduce a la pérdida de memoria?

En caso afirmativo, ¿cómo resolver este problema?

Respuesta

19

el método jQuery html intenta evitar pérdidas de memoria mediante la eliminación de controladores de eventos para los elementos que se eliminan como resultado de llamar al .html('') en un objeto jQuery.

Desde la fuente 1.4.2

html: function(value) { 
    if (value === undefined) { 
     return this[0] && this[0].nodeType === 1 ? 
     this[0].innerHTML.replace(rinlinejQuery, "") : 
      null; 
    } 
    // See if we can take a shortcut and just use innerHTML 
    // THE RELEVANT PART 
    else if (typeof value === "string" && !rnocache.test(value) && 
     (jQuery.support.leadingWhitespace || !rleadingWhitespace.test(value)) && 
     !wrapMap[ (rtagName.exec(value) || ["", ""])[1].toLowerCase() ]) { 

     value = value.replace(rxhtmlTag, fcloseTag); 

     try { 
      for (var i = 0, l = this.length; i < l; i++) { 
       // Remove element nodes and prevent memory leaks 
       if (this[i].nodeType === 1) { 
        jQuery.cleanData(this[i].getElementsByTagName("*")); 
        this[i].innerHTML = value; 
       } 
      } 

     // If using innerHTML throws an exception, use the fallback method 
     } 
     catch(e) { 
      this.empty().append(value); 
     } 
    } 
    else if (jQuery.isFunction(value)) { 
     this.each(function(i){ 
      var self = jQuery(this), old = self.html(); 
      self.empty().append(function(){ 
       return value.call(this, i, old); 
      }); 
     }); 

    } 
    else { 
     this.empty().append(value); 
    } 
    return this; 
} 

Podemos ver que la función se llama jQuery.cleanData(). Aquí es la fuente para que

cleanData: function(elems) { 
    var data, id, cache = jQuery.cache, 
     special = jQuery.event.special, 
     deleteExpando = jQuery.support.deleteExpando; 

    for (var i = 0, elem; (elem = elems[i]) != null; i++) { 
     id = elem[ jQuery.expando ]; 

     if (id) { 
      data = cache[ id ]; 

      if (data.events) { 
       for (var type in data.events) { 
        if (special[ type ]) { 
         jQuery.event.remove(elem, type); 

        } else { 
         removeEvent(elem, type, data.handle); 
        } 
       } 
      } 

      if (deleteExpando) { 
       delete elem[ jQuery.expando ]; 

      } else if (elem.removeAttribute) { 
       elem.removeAttribute(jQuery.expando); 
      } 

      delete cache[ id ]; 
     } 
    } 
} 

Esto se ve en el objeto jQuery.cache de las propiedades de tipo de eventos en los eventos de objetos propiedad del objeto de datos relativos a cada elemento que se eliminará cuando se llama a .html('') y los elimina.

Para explicar básicamente cómo el evento estándar de las obras de unión, cuando una función está ligada a un controlador de un evento levantado sobre un elemento usando jQuery, se añade un objeto de datos como una propiedad al objeto jQuery.cache. Este objeto de datos contiene un objeto de propiedad de eventos que tendrá una propiedad creada con un nombre que coincida con el tipo de evento al que desea vincular la función del controlador de eventos. esta propiedad contendrá una serie de funciones que se deben invocar cuando se genera el evento en el elemento, por lo que la función del controlador de eventos se agrega a esta matriz.Si esta es la primera función del controlador de eventos para el tipo y elemento de evento en cuestión, la función jQuery.event.handle con una llamada a aplicar (utilizando el elemento como el contexto de manera que this en el contexto de ejecución de la función se referirá al elemento) se registra con el navegador usando addEventListener/attachEvent.

Cuando se produce un evento, la función jQuery.event.handle llamará a todas las funciones de la matriz en la propiedad del objeto de propiedad de eventos del objeto de datos que coincida con el tipo de evento y el elemento sobre el que se generó el evento.

Por lo tanto, en resumen, html('') no debe causar pérdidas de memoria ya que hay una serie de medidas defensivas para evitarlas.

+0

@russ creo que tienes razón ayer hice muchas pruebas y llegué a la conclusión de que .html (''); también evitará fugas de memoria, mientras que JS's innerHTML = '', causará fugas de memoria. me pregunto si jquery hace una lista interna de elementos a los que hemos llamado función $() bind() (es decir, enlaza algún evento), para que en window.unload podamos eliminar esos eventos o jquery lo haga automáticamente. ¿Alguno tiene idea de esto? –

+0

jQuery vincula automáticamente una función para eliminar manejadores de eventos en 'window.onunload' como parte de la ejecución del script inicial. Encontrará el código correspondiente en la fuente 1.4.2 justo antes del bloque del código Sizzle - http://code.jquery.com/jquery-1.4.2.js :) –

+0

está en lo correcto, incluso jquery 1.3.2 versión eventos claros vinculados a elementos en la descarga de la ventana. , así que creo que no tenemos que escribir nuestros códigos para borrar los eventos enlazados para evitar fugas de memoria. thanx –

2

No se puede comentar el problema de fuga pero simplemente podría usar .empty() en lugar de .html(''). De esa forma limpiarías el innerHTML y eliminarías cualquier controlador de eventos enlazados.

1

Siempre se puede utilizar $('#parent span').unbind(); sólo para estar seguro

2

Sí, porque jQuery mantiene una lista de los controladores de eventos unidos para hacer desenganche sean más fáciles y con el fin de desenganchar explícitamente para usted cuando se descarga la página (que funciona en torno a una pérdida de memoria más grave en IE). (Lo mismo ocurre con Prototype, no puede hablar de otras librerías). La solución es desengancharlas antes de eliminar los elementos (ya sea directamente o a través del empty).

0

Como se refiere constantemente a $ ('# parent'), debe crear una referencia a ese objeto en el ámbito global para que jQuery no busque constantemente el objeto en cada solicitud. Al hacer esto, esencialmente está almacenando en caché la referencia al objeto, lo que reducirá enormemente el uso de memoria.

_parent = $('#parent'); 

...

function(){ _parent.append('<span>1</span>'); } 

Editar: Cogí este consejo de este artículo en jQuery Performance Rules

+1

¿Tremendamente? ¿Ha perfilado el uso de memoria antes y después para verificar este reclamo? – PatrikAkerstrand

+0

Estaba teniendo tantas fugas de memoria en una aplicación jQuery que MSIE se cerraría después de 10 minutos. Después de realizar este único ajuste, las fugas desaparecieron y la memoria se estabilizó en una cantidad razonable. –

+3

No lo llamaría una "fuga" de memoria que no almacena objetos de jQuery en el caché, sino una práctica que podría conducir a un mal rendimiento, que es lo que parece que estaba experimentando en IE, la familia de navegadores que generalmente ha implementado menos de métodos nativos ('Selectors API',' document.getElementsByClassName') que otros navegadores y, por lo tanto, necesita confiar en una implementación transversal JavaScript DOM para lograr el mismo resultado. También trataría de evitar la contaminación global del espacio de nombres mediante el análisis de las variables en las que almacena los objetos jQuery de forma tan local como sea posible. –

Cuestiones relacionadas