Resueltomemoria riesgo de fugas en JavaScript cierres
Hay una gran cantidad de información contradictoria en la web, con respecto a este tema. Gracias a @John, me las arreglé para pensar que los cierres (como se usan a continuación) no son la causa de las pérdidas de memoria, y que, incluso en IE8, no son tan comunes como dicen las personas. De hecho, solo hubo una fuga en mi código, que no resultó tan difícil de solucionar.
A partir de ahora, mi respuesta a esta pregunta será:
yo sepa, las fugas IE8 única vez, es cuando se unen eventos/manipuladores se establecen en el objeto global. (window.onload
, window.onbeforeunload
, ...). Para evitar esto, vea mi respuesta a continuación.
enorme ACTUALIZACIÓN:
estoy completamente perdido ahora ... Después de cavar a través de artículos y tuts tanto antiguos como nuevos algún tiempo, me quedo con al menos un gigantesco contradicción. Mientras que uno de LA JavaScript de (Douglas Crockford) Guru dice:
Desde IE es incapaz de hacer su trabajo y recuperar los ciclos, que cae sobre nosotros para hacerlo. Si rompemos los ciclos de manera explícita, IE podrá reclamar la memoria. Según Microsoft, los cierres son la causa de pérdidas de memoria. Esto, por supuesto, está muy mal, pero lleva a Microsoft a dar muy malos consejos a los programadores sobre cómo lidiar con los errores de Microsoft. Resulta que es fácil romper los ciclos en el lado DOM. Es prácticamente imposible romperlos en el lado de JScript.
Y como @freakish señaló que mis fragmentos a continuación son similares al funcionamiento interno de jQuery, me sentí bastante seguro de que mi solución no causaba pérdidas de memoria. Al mismo tiempo encontré this MSDN page, donde la sección Circular References with Closures
fue de particular interés para mí. La siguiente figura es más o menos una representación esquemática de cómo funciona mi código, ¿no es así:
La única diferencia es que tengo el sentido común de no unir mis detectores de eventos a los propios elementos.
Todo lo mismo Douggie es bastante inequívoco: los cierres no son la fuente de mem-leaks en IE. Esta contradicción no me deja ni idea de quién tiene la razón.
También he descubierto que el problema de fuga no está completamente resuelto en IE9 tampoco (no se puede encontrar el enlace ATM).
Una última cosa: También he llegado a saber que IE maneja el DOM fuera del motor de JScript, que me pone en un punto de molestia cuando cambio los hijos de un elemento <select>
, basado en una petición AJAX :
function changeSeason(e)
{
var xhr,sendVal,targetID;
e = e || window.event;//(IE...
targetID = this.id.replace(/commonSourceFragment/,'commonTargetFragment');//fooHomeSelect -> barHomeSelect
sendVal = this.options[this.selectedIndex].innerHTML.trim().substring(0,1);
xhr = prepareAjax(false,(function(t)
{
return function()
{
reusableCallback.apply(this,[t]);
}
})(document.getElementById(targetID)),'/index/ajax');
xhr({data:{newSelect:sendVal}});
}
function reusableCallback(elem)
{
if (this.readyState === 4 && this.status === 200)
{
var data = JSON.parse(this.responseText);
elem.innerHTML = '<option>' + data.theArray.join('</option><option>') + '</option>';
}
}
Si IE realmente se las arregla el DOM como si el motor de JScript no estuviera allí, ¿cuáles son las probabilidades de que los elementos de opción no se libera con este código?
He agregado deliberadamente este fragmento como ejemplo, porque en este caso paso variables que forman parte del ámbito de cierre como argumento para una función global.No pude encontrar ninguna documentación sobre esta práctica, pero en base a la documentación aportada por Miscrosoft, se debería romper todas las referencias circulares que podrían ocurrir, ¿no?
Advertencia: larga pregunta ... (lo siento )
He escrito un par de bastante grandes JavaScript para hacer llamadas Ajax en mi aplicación web. con el fin de evitar toneladas de devoluciones de llamada y eventos, estoy tomando el máximo provecho de delegación de eventos y cierres. Ahora he escrito una función que me hace pensar en posibles fugas de memoria. Aunque sé IE> 8 se refiere a cierres mucho mejor que sus predecesores, es política de la empresa para apoyar el IE 8 lo mismo.
A continuación he incluido un ejemplo de lo que estoy hablando de, here se puede encontrar un ejemplo similar, aunque no utiliza Ajax, pero un setTimeout, el resultado es más o menos lo mismo. (Puede, por supuesto, obviar el código de abajo, a la pregunta en sí misma)
El código que tengo en mente es la siguiente:
function prepareAjax(callback,method,url)
{
method = method || 'POST';
callback = callback || success;//a default CB, just logs/alerts the response
url = url || getUrl();//makes default url /currentController/ajax
var xhr = createXHRObject();//try{}catch etc...
xhr.open(method,url,true);
xhr.setRequestMethod('X-Requested-with','XMLHttpRequest');
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
xhr.setRequestHeader('Accept','*/*');
xhr.onreadystatechange = function()
{
callback.apply(xhr);
}
return function(data)
{
//do some checks on data before sending: data.hasOwnProperty('user') etc...
xhr.send(data);
}
}
Todo material bastante sencillo, a excepción de la onreadystatechange
de devolución de llamada. Me di cuenta de algunos problemas con IE cuando se enlaza directamente al controlador: xhr.onreadystatechange = callback;
, por lo tanto, la función anónima. No sé por qué, pero creo que esta es la forma más fácil de hacerlo funcionar.
Como ya he dicho, estoy usando un montón de delegación de eventos, por lo que se puede imaginar que puede resultar útil para tener acceso al elemento/evento real que disparó la llamada AJAX. Así que tengo algunos controladores de eventos que se ven así:
function handleClick(e)
{
var target,parent,data,i;
e = e || window.event;
target = e.target || e.srcElement;
if (target.tagName.toLowerCase() !== 'input' && target.className !== 'delegateMe')
{
return true;
}
parent = target;
while(parent.tagName.toLowerCase() !== 'tr')
{
parent = parent.parentNode;
}
data = {};
for(i=0;i<parent.cells;i++)
{
data[parent.cells[i].className] = parent.cells[i].innerHTML;
}
//data looks something like {name:'Bar',firstName:'Foo',title:'Mr.'}
i = prepareAjax((function(t)
{
return function()
{
if (this.readyState === 4 && this.status === 200)
{
//check responseText and, if ok:
t.setAttribute('disabled','disabled');
}
}
})(target));
i(data);
}
Como se puede ver, el onreadystatechange
devolución de llamada es el valor de retorno de una función, que proporciona la referencia al elemento target
cuando la devolución de llamada. Gracias a delegación de eventos, ya no tiene que preocuparse acerca de los eventos que podrían estar ligados a ese elemento, cuando decido sacarlo de la DOM (que a veces lo hago).
En mi opinión, sin embargo, el objeto de la llamada de la función de devolución de llamada podría resultar demasiado para motor de JScript de IE y su recolector de basura:
Evento ==> == manejador> prepareAjax es una secuencia de llamada bastante normal, pero el argumento de devolución de llamada:
[anon. func (argumento t = objetivo) devuelve anon. F (t tiene acceso a lo que en las referencias volver atrás para apuntar)]
===> pasa a una función de devolución de llamada anon, llamada usando .apply método para el objeto XHR, a su vez, una variable privadaa la prepareAjax función
He probado esta "construcción" en FF y cromo. Funciona muy bien allí, sino que este tipo de pila de llamadas de cierre durante el cierre durante el cierre, en cada ocasión que pasa una referencia a un elemento DOM ser un problema en IE (especialmente las versiones anteriores a IE9)?
No, no voy a usar jQuery o otras librerías. Me gusta JS puro, y quiero saber todo lo que pueda sobre este lenguaje seriamente subestimado.Los fragmentos de código no son ejemplos reales de copiar y pegar, pero proporcionan, IMO, una buena representación de cómo estoy usando delegación, cierres y devoluciones de llamadas a lo largo de mi secuencia de comandos. Entonces, si alguna sintaxis no es correcta, no dude en corregirla, pero de eso no se trata esta pregunta, por supuesto.
No sé qué 'xhr.onreadystatechange = callback' no funciona para usted. Interesante. Pero en realidad esta parte no debería importar en absoluto, la pérdida de memoria es lo más interesante. Nunca he visto tal comportamiento, aunque no trabajé con IE <9 por bastante tiempo. – freakish
BTW: No tiene que usar jQuery, pero puede consultar su código fuente. :) Lo hice y parece que no están haciendo nada diferente a lo que eres. No debería dar pérdidas de memoria. ¿Estás seguro de que no tienes una referencia al 'xhr' en alguna parte? O al resultado 'prepareAjax'? – freakish
@freakish: no espero que 'xhr.onreadystatechange = callback' sea la causa de los problemas de memoria (si hay alguno). Lo mencioné porque a mí también me resulta extraño el comportamiento de IE. Pero hace tiempo que he perdido la esperanza de que IE se comporte lógicamente. Según usted, este código puede causar problemas de memoria, eso es lo que me preocupa –