removeattribute removeattr disabled attribute javascript jquery performance memory-leaks garbage-collection

javascript - disabled - removeattribute jquery



Patrones de fuga de memoria jQuery y causas (2)

Por lo que entiendo, la administración de la memoria en javascript se lleva a cabo mediante el recuento de referencias: aunque todavía existe una referencia a un objeto, no se desasignará. Esto significa que la creación de una pérdida de memoria en una aplicación de una sola página es trivial, y puede tropezar con las de uso provenientes de un fondo java. Esto no es específico de JQuery. Tome el siguiente código, por ejemplo:

function MyObject = function(){ var _this = this; this.count = 0; this.getAndIncrement = function(){ _this.count++; return _this.count; } } for(var i = 0; i < 10000; i++){ var obj = new MyObject(); obj.getAndIncrement(); }

Se verá normal hasta que vea el uso de la memoria. Las instancias de MyObject nunca se desasignan mientras la página está activa, debido al puntero "_this" (aumenta el valor máximo de i para verlo más dramáticamente). (En versiones anteriores de IE, nunca fueron desasignados hasta que el programa finaliza). Dado que los objetos javascript pueden compartirse entre fotogramas (no recomiendo probar esto, ya que es muy temperamental), hay casos en los que incluso en un navegador moderno JavaScript los objetos pueden permanecer mucho más tiempo de lo que deberían.

En el contexto de jquery, las referencias a menudo se almacenan para guardar la sobrecarga de la búsqueda dom, por ejemplo:

function run(){ var domObjects = $(".myClass"); domObjects.click(function(){ domObjects.addClass(".myOtherClass"); }); }

Este código se mantendrá en domObject (y todos sus contenidos) para siempre, debido a la referencia a él en la función de devolución de llamada.

Si los escritores de jquery han omitido instancias como esta internamente, entonces la biblioteca en sí tendrá fugas, pero más a menudo es el código del cliente.

El segundo ejemplo se puede solucionar borrando explícitamente el puntero cuando ya no se requiera:

function run(){ var domObjects = $(".myClass"); domObjects.click(function(){ if(domObjects){ domObjects.addClass(".myOtherClass"); domObjects = null; } }); }

o haciendo la búsqueda de nuevo:

function run(){ $(".myClass").click(function(){ $(".myClass").addClass(".myOtherClass"); }); }

Una buena regla empírica es tener cuidado al definir las funciones de devolución de llamada y evitar el exceso de anidación cuando sea posible.

Editar: como se señaló en los comentarios de Erik, también podría usar este puntero para evitar la búsqueda dom innecesaria:

function run(){ $(".myClass").click(function(){ $(this).addClass(".myOtherClass"); }); }

¿Cuáles son algunos de los problemas estándar o patrones de codificación en jQuery que conducen a pérdidas de memoria?

He visto una serie de preguntas relacionadas con la llamada ajax () o la eliminación de jsonp o DOM en StackOverflow. La mayoría de las preguntas sobre la fuga de memoria de jQuery están enfocadas en problemas específicos o navegadores y sería bueno tener una lista de los patrones de fuga de memoria estándar en jQuery.

Aquí hay algunas preguntas relacionadas sobre SO:

Recursos en la web:


Contribuiré con un antipatrón aquí, que es la fuga de "referencia a mitad de cadena".

Una de las ventajas de jQuery es su API de encadenamiento, que le permite continuar cambiando, filtrando y manipulando los elementos:

$(".message").addClass("unread").find(".author").addClass("noob");

Al final de esa cadena, tiene un objeto jQuery con todos los elementos ".message .author", pero ese objeto hace referencia a los objetos originales ".message" y se opone a ellos. Puede acceder a ellos a través del método .end() y hacerles algo:

$(".message") .find(".author") .addClass("prolific") .end() .addClass("unread");

Ahora, cuando se usa de esta manera, no hay problemas con las fugas. Sin embargo, si asigna el resultado de una cadena a una variable que tiene una larga vida útil, las referencias anteriores a conjuntos anteriores permanecen y no se pueden recolectar basura hasta que la variable salga del alcance. Si esa variable es global, las referencias nunca salen del alcance.

Entonces, por ejemplo, digamos que leyó en una publicación de blog de 2008 que $("a").find("b") era "más eficiente" que $("ab") (aunque no es digno de siquiera pensar en tal una micro-optimización). Usted decide que necesita una página global para tener una lista de autores para que haga esto:

authors = $(".message").find(".author");

Ahora tiene un objeto jQuery con la lista de autores, pero también se refiere a un objeto jQuery que es la lista completa de mensajes. Probablemente nunca lo usarás o incluso sabrás que está ahí, y está ocupando la memoria.

Tenga en cuenta que las filtraciones solo pueden ocurrir con los métodos que seleccionan nuevos elementos de un conjunto existente, como .find , .filter , .children , etc. Los documentos indican cuándo se devuelve un nuevo conjunto. El simple uso de una API de encadenamiento no causa una fuga si la cadena tiene métodos simples que no filtran, como .css , por lo que está bien:

authors = $(".message .author").addClass("prolific");