Предотвратить утечки памяти в этом коде JavaScript? - PullRequest
2 голосов
/ 05 сентября 2010

У меня есть страница администратора Django для вложенного списка категорий, например: alt text

Я написал этот скрипт для сортировки списка и представления его в иерархическом порядке:

{% 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 %}

...Сценарий работает нормально, и список выглядит следующим образом: alt text

Мой вопрос: я чувствую, что сценарий подвержен утечкам памяти в UA со слабой сборкой мусора из-за циклических ссылок, созданных родителем/ детские вещи.Это то, что я должен беспокоиться?Есть ли лучший способ написать сценарий?Должен ли я удалять кучу вещей в конце скрипта, и если да, то что?

Ответы [ 2 ]

1 голос
/ 05 сентября 2010

Я вижу некоторые незначительные утечки, IE сборщик мусора имеет проблему с очисткой ссылок на живые узлы внутри функций.Это связано с тем, что DOM и JavaScript имеют свой собственный сборщик мусора и, по сути, они не хотят вступать в схватку с друг другом за ссылку.

Поскольку вы вызываете этот скрипт один раз на страницу?Утечка памяти незначительна и может фактически игнорироваться, если только люди не открывают более 100 страниц за один сеанс.Хотя очистка приятнее.

{% 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 %}

Было немного запутанно разбираться, поскольку имена ваших объектов JS подразумевают, что они являются элементами DOM = P, но, думаю, я все понял правильно.Но вы, возможно, захотите пройтись по коду и обнулить другие элементы DOM, которые я, возможно, пропустил (как только вы закончите с ними, конечно).

1 голос
/ 05 сентября 2010

Сценарий не , кажется, содержит какие-либо серьезные утечки памяти, поскольку он не оставляет никаких функций (например, cb) после прогулки, Таким образом, сборщик мусора должен успешно собрать весь созданный мусор.

Однако вы можете использовать большого объема памяти во время выполнения, если число итераций действительно велико.

[Amatuer статья, которую я написал об этом некоторое время назад] http://stefan.artspace44.com/javascript/memory-leaks/

...