2010-09-05 3 views
2

J'ai une page d'administration de Django pour une liste de catégories imbriquées comme ceci: alt textPrévenir les fuites de mémoire dans ce code javascript?

J'ai écrit ce script pour trier la liste et présenter hiérarchiquement:

{% extends "admin/change_list.html" %} 
{% load i18n %} 
{% block footer %} 

<script> 
(function(){ 

    var rows=document.getElementById('result_list').getElementsByTagName('tr'), 
     table=rows[1].parentNode||rows[1].parentElement, 
     i=0, r,  // skip the first row 
     data={};  // store category data 

    while (r=rows[++i]) { 
    var catName=r.getElementsByTagName('a')[0], 
     k=catName.innerHTML, 
     opts=r.getElementsByTagName('select')[0], 
     j=-1, opt; 
    while (opt=opts[++j]) { 
     if (!opt.selected) continue; 
     data[k] = { 
     title:  k, 
     children:  {}, 
     parentName: opt.innerHTML, 
     parentId:  opt.value, 
     catName:  catName, 
     row:   r 
     } 
    } 
    } 

    for (var sub in data) { 
    if (data[sub].parentName == sub) continue; 
    for (var sup in data) { 
     if (sup == data[sub].parentName) { 
     data[sup].children[sub]=data[sub]; 
     data[sub].parent = data[sup]; 
     break; 
     } 
    } 
    } 

    var alt = 0; 
    for (var leaf in data) { 
    if (data[leaf].parentName != leaf) continue; 
    walk(data[leaf], leaf, function (node, nodeName) { 
     var n=node, t=n.title; 
     while (n=n.parent) { 
     t = ' &middot; &nbsp;' + t; 
     } 
     node.catName.innerHTML = t; 
     node.row['class']=node.row['className']='row'+alt++%2; 
     table.removeChild(node.row); 
     table.appendChild(node.row); 
    }); 
    } 

    function walk (leaf, leafName, cb) { 
    if (cb) cb(leaf, leafName); 
    leaf.ready = true; 
    for (var kid in leaf.children) { 
     if (leaf.children[kid].ready) continue; 
     walk(leaf.children[kid], kid, cb); 
    } 
    } 

}()); 
</script> 

{% endblock %} 

... le script fonctionne très bien et la liste ressemble à ceci: alt text

Ma question est: Je me sens comme le script est sujette à des fuites de mémoire dans les UAs avec faible garbage collection en raison des références circulaires créées par le parent/enfant st uff. Est-ce quelque chose dont je devrais m'inquiéter? Y a-t-il une meilleure façon d'écrire le script? Dois-je supprimer un tas de choses à la fin du script, et si oui, quoi?

+1

Vous "avez l'impression" que le script est sujet à des fuites de mémoire, ou vous avez * observé * des fuites de mémoire? –

+0

Je n'ai pas observé tout, mais je me sens comme IE pourrait faire quelque chose de stupide comme pas garbage recueillir quelque chose lorsque la page navigue. Malheureusement, je n'ai aucun moyen de tester cet ATM. –

+0

est-ce que vous OS vous permet d'observer l'utilisation de RAM au fil du temps? Si oui, je suis sûr que vous pourriez mettre en place un test primitif pour voir si des quantités non désirées de mémoire sont fuites ... – Stefan

Répondre

1

Je vois quelques fuites mineures, garbage collector IE a un problème de nettoyage références de nœud actif à l'intérieur des fonctions. C'est parce que le DOM et JavaScript ont tous deux leur propre garbage collector et, fondamentalement, ils ne veulent pas se battre entre eux sur une référence.

Depuis que vous appelez ce script une fois par page? La fuite de mémoire est minime et peut être ignorée, sauf si les utilisateurs ouvrent plus de 100 pages en une seule session. Le nettoyage est plus agréable.

{% extends "admin/change_list.html" %} 
{% load i18n %} 
{% block footer %} 

<script> 
(function(){ 

    var rows=document.getElementById('result_list').getElementsByTagName('tr'), 
     table={}, 
     i=0, r,  // skip the first row 
     data={};  // store category data 

    // table is now a JS object with a el reference to an element. 
    table.el = rows[1].parentNode||rows[1].parentElement; 

    while (r=rows[++i]) { // you skip the first row, that correct? Else use i++ 
    var catName=r.getElementsByTagName('a')[0], 
     k=catName.innerHTML, 
     opts=r.getElementsByTagName('select')[0], 
     j=-1, opt; 
    while (opt=opts[++j]) { 
     if (!opt.selected) continue; 
     data[k] = { 
     title:  k, 
     children:  {}, 
     parentName: opt.innerHTML, 
     parentId:  opt.value, 
     catName:  catName, 
     row:   r 
     } 
    } 
    } 
    // nullify node references 
    r = catName = opt = rows = null; 

    for (var sub in data) { 
    if (data[sub].parentName == sub) continue; 
    for (var sup in data) { 
     if (sup == data[sub].parentName) { 
     data[sup].children[sub]=data[sub]; 
     data[sub].parent = data[sup]; 
     break; 
     } 
    } 
    } 

    var alt = 0; 
    for (var leaf in data) { 
    if (data[leaf].parentName != leaf) continue; 
    walk(data[leaf], leaf, function (node, nodeName) { 
     var n=node, t=n.title; 
     while (n=n.parent) { 
     t = ' &middot; &nbsp;' + t; 
     } 
     node.catName.innerHTML = t; 
     node.row['class']=node.row['className']='row'+alt++%2; 
     // if table wasn't a JS object, this closure would not have been cleaned up. 
     // a refence to table is kept, not to a live DOM element. 
     table.el.removeChild(node.row); 
     table.el.appendChild(node.row); 
    }); 
    } 


    function walk (leaf, leafName, cb) { 
    if (cb) cb(leaf, leafName); 
    leaf.ready = true; 
    for (var kid in leaf.children) { 
     if (leaf.children[kid].ready) continue; 
     walk(leaf.children[kid], kid, cb); 
    } 
    } 

}()); 
</script> 

{% endblock %} 

était un peu déroutant pour trier, puisque vos noms d'objets JS impliquent qu'ils sont des éléments DOM = P mais je pense que je pensais que ce correctement. Mais vous voudrez peut-être passer en revue le code et annuler d'autres éléments DOM que j'aurais pu ignorer (une fois que vous en aurez fini avec eux).

+0

Merci pour votre réponse. Question: could 'r',' catName', 'opt', et' rows' (toutes les variables que vous avez annulées) pourraient être transformés en propriétés d'un objet, comme table.el, au lieu d'annuler les références? Je suis curieux de savoir pourquoi vous avez pris deux approches différentes pour 'table' et pour les autres. –

+0

Les quatre premiers ne sont utilisés que dans une boucle, donc vous ne voyez pas pourquoi vous voudriez les conserver sur un objet, la table que vous réutilisez dans une fonction anonyme via une fermeture passée à walk(). La boucle où s'appelle walk crée une fonction à chaque itération, ce qui aurait pu laisser beaucoup de références potentielles d'éléments vivants.Je l'ai collé sur un objet afin que la fonction ne connaisse que la référence de l'objet JS et que l'objet contienne la référence de l'élément DOM. – BGerrissen

+0

En fait, vous pouvez aussi déclarer la fonction passée à walk() avant la boucle afin de ne pas créer une fonction à chaque itération, mais c'est une optimisation mineure. – BGerrissen

1

Le script ne semble contenir toute fuite de mémoire grave, car il ne laisse pas de fonctions (comme cb) autour après la promenade . Donc, le ramasse-miettes devrait ramasser avec succès toutes les ordures créées.

Vous pouvez cependant avoir une utilisation élevée de la mémoire pendant l'exécution si le nombre d'itérations est vraiment élevé.

[article Amatuer je l'ai écrit sur ce il y a quelque temps] http://stefan.artspace44.com/javascript/memory-leaks/

Questions connexes