risoltoMemoria rischio di perdite in JavaScript chiusure
C'è un sacco di informazioni contraddittorie sul web, per quanto riguarda questo argomento. Grazie a @John, sono riuscito a capire che le chiusure (come usate di seguito) non sono la causa delle perdite di memoria, e che, anche in IE8, non sono così comuni come affermano le persone. In effetti c'era solo una perdita che si è verificata nel mio codice, il che non è stato così difficile da risolvere.
Da ora in poi, la mia risposta a questa domanda sarà:
per quanto ne so, l'unica volta che le perdite IE8, è quando sono attaccati gli eventi/gestori sono impostati sul oggetto globale. (window.onload
, window.onbeforeunload
, ...). Per aggirare questo, vedi la mia risposta qui sotto.
ENORME UPDATE:
sto completamente perso ora ... Dopo un po 'di tempo a scavare attraverso articoli e tuts vecchi e nuovi, io sono rimasto con almeno un humongous contraddizione. Mentre uno dei di LA JavaScript Guru (Douglas Crockford) dice:
Dal IE è in grado di fare il suo lavoro e recuperare i cicli, si cade su di noi per farlo. Se interrompiamo esplicitamente i cicli, allora IE sarà in grado di recuperare la memoria. Secondo Microsoft, le chiusure sono la causa delle perdite di memoria. Questo è ovviamente profondamente sbagliato, ma porta a Microsoft dare consigli molto male ai programmatori su come affrontare i bug di Microsoft. Risulta che è facile rompere i cicli sul lato DOM. È praticamente impossibile romperli sul lato JScript.
E come @freakish ha sottolineato che i miei frammenti di seguito sono simili ai meccanismi interni di jQuery, mi sentivo piuttosto sicuro che la mia soluzione non causasse perdite di memoria. Allo stesso tempo ho trovato this MSDN page, dove la sezione Circular References with Closures
era di particolare interesse per me. La figura qui sotto è praticamente una rappresentazione schematica di come funziona il mio codice, non è vero:
L'unica differenza è che ho il buon senso di non attaccare i miei ascoltatori di eventi per gli elementi stessi.
Tutti uguali Douggie è abbastanza inequivocabile: chiusure non sono la fonte di perdite di mem in IE. Questa contraddizione mi lascia senza tracce su chi ha ragione.
Ho anche scoperto che il problema di perdita non è completamente risolto in IE9 (non è possibile trovare il collegamento ATM).
Un'ultima cosa: ho anche venuto a sapere che IE gestisce il DOM all'esterno del motore JScript, che mi mette in un po 'di fastidio quando cambio i figli di un elemento <select>
, sulla base di una richiesta 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>';
}
}
Se IE realmente riesce il DOM come se il motore JScript non ci fosse, quali sono le probabilità che gli elementi di opzione non vengono deallocate utilizzando questo codice?
Ho deliberatamente aggiunto questo snippet come esempio, perché in questo caso sto passando variabili che fanno parte dell'ambito di chiusura come argomento di una funzione globale.Non sono riuscito a trovare alcuna documentazione su questa pratica, ma sulla base della documentazione fornita da Miscrosoft, dovrebbe interrompere qualsiasi riferimento circolare che potrebbe verificarsi, non è vero?
Attenzione: lunga domanda ... (scusate )
Ho scritto un paio di abbastanza grandi JavaScript per effettuare chiamate Ajax nella mia applicazione web. Per evitare tonnellate di callback ed eventi, sto sfruttando appieno la delega e la chiusura degli eventi. Ora ho scritto una funzione che mi chiede quali possibili perdite di memoria. Anche se so che IE> 8 riguarda le chiusure molto meglio dei suoi predecessori, è la politica aziendale a supportare lo IE 8 allo stesso modo.
Di seguito ho fornito un esempio di quello di cui sto parlando, here è possibile trovare un esempio simile, sebbene non utilizzi ajax, ma un setTimeout, il risultato è praticamente lo stesso. (È possibile, naturalmente saltare il codice qui sotto, alla domanda stessa)
Il codice che ho in mente è questa:
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);
}
}
Tutti roba abbastanza straight-forward, fatta eccezione per la onreadystatechange
callback. Ho notato alcuni problemi con IE durante l'associazione diretta del gestore: xhr.onreadystatechange = callback;
, quindi la funzione anonima. Non so perché, ma ho trovato questo il modo più semplice per farlo funzionare.
Come ho detto, sto usando un sacco di deleghe di eventi, quindi puoi immaginare che possa rivelarsi utile avere accesso all'elemento/evento reale che ha generato la chiamata ajax. Così ho alcuni gestori di eventi che assomigliano a questo:
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);
}
Come si può vedere, la richiamata onreadystatechange
è il valore di ritorno di una funzione, che fornisce il riferimento all'elemento target
quando il callback viene chiamata. Grazie alla delega degli eventi, non devo più preoccuparmi degli eventi che potrebbero essere associati a quell'elemento, quando decido di rimuoverlo dal DOM (cosa che a volte faccio).
A mio parere, tuttavia, l'oggetto richiamo della funzione di callback potrebbe rivelarsi troppo per il motore JScript di IE e la sua garbage collector:
evento ==> handler ==> prepareAjax è un abbastanza normale sequenza di chiamata, ma l'argomento del callback:
[anon. func (argomento t = target) restituisce anon. F (t ha accesso a cui nei rif tornare indietro a bersaglio)]
===> passato a una funzione anon richiamata, chiamata utilizzando .Applicare metodo per l'oggetto XHR, a sua volta privato variabile al prepareAjax funzione
ho testato questa "costruzione" in FF e Chrome. Funziona benissimo lì, ma questo tipo di callstack di chiusura alla chiusura alla chiusura, ogni volta che passa un riferimento ad un elemento DOM è un problema in IE (specialmente versioni precedenti a IE9)?
No, non ho intenzione di utilizzare jQuery o altre librerie. Mi piace il puro JS e voglio sapere il più possibile su questo linguaggio gravemente sottovalutato.I frammenti di codice non sono veri esempi di copia-incolla, ma forniscono, IMO, una buona rappresentazione di come sto usando la delega, chiusure e callback in tutto il mio script. Quindi se qualche sintassi non è corretta, sentiti libero di correggerla, ma non è di questo che si tratta, naturalmente.
Non so il motivo per cui 'xhr.onreadystatechange = callback' non funziona per voi. Interessante. Ma in realtà questa parte non dovrebbe importare affatto, la perdita di memoria è la cosa più interessante. Non ho mai visto un comportamento del genere, anche se non ho lavorato con IE <9 per un po 'di tempo. – freakish
BTW: Non devi usare jQuery, ma puoi guardare il suo codice sorgente. :) L'ho fatto e sembra che non stiano facendo nulla di diverso allora. Non dovrebbe dare perdite di memoria. Sei sicuro di non avere un riferimento al 'xhr' da qualche parte? O per il risultato 'prepareAjax'? – freakish
@freakish: non mi aspetto che 'xhr.onreadystatechange = callback' sia la causa dei problemi di memoria (se ce ne sono). L'ho menzionato perché anch'io trovo strano comportamento da parte di IE. Ma ho perso da tempo la speranza che IE si comporti logicamente. Secondo te, questo codice potrebbe causare problemi di memoria, questo è quello che mi preoccupa per –