防止这段 JavaScript 代码中的内存泄漏?

发布于 2024-09-18 11:16:35 字数 2080 浏览 5 评论 0原文

我有一个 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 中很容易出现内存泄漏,因为循环由父/子内容创建的引用。这是我应该担心的事情吗?有没有更好的方法来编写脚本?我应该在脚本末尾删除一堆内容吗?如果是,该怎么办?

I have a Django admin page for a nested category list like this:
alt text

I wrote this script to sort the list and present it hierarchically:

{% 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 = ' ·  ' + 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 %}

...the script runs fine and the list looks like this:
alt text

My question is: I feel like the script is prone to memory leaks in UAs with weak garbage collection because of the circular references created by the parent / child stuff. Is this something I should be worried about? Is there a better way to write the script? Should I be deleting a bunch of stuff at the end of the script, and if so, what?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好菇凉咱不稀罕他 2024-09-25 11:16:35

我看到一些小泄漏,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 = ' ·  ' + 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 元素(当然,一旦您完成了它们)。

I see some minor leaks, IE garbage collector has a problem cleaning live node references inside functions. This is because the DOM and JavaScript both have it's own garbage collector and basically, they don't want to pick a fight with eachother over a reference.

Since you call this script once per page? The memory leak is minute and can actually be ignored, unless people open 100+ pages in one session. Cleaning up is nicer though.

{% 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 = ' ·  ' + 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 %}

Was a bit confusing to sort out, since your JS object names imply they are DOM elements =P but I think I figured it out correctly. But you might want to go over the code and nullify other DOM elements I might've overlooked (once you're done with them ofcourse).

我不吻晚风 2024-09-25 11:16:35

该脚本似乎不包含任何严重的内存泄漏,因为它在walk之后没有留下任何函数(如 cb ) 。因此垃圾收集器应该成功收集所有创建的垃圾。

然而,如果迭代次数非常高,则在执行期间可能会出现较高的内存使用率。

[我不久前写过关于此的业余文章]
http://stefan.artspace44.com/javascript/memory-leaks/

The script doesn't seem to contain any serious memory leaks, as it doesn't leave any functions ( like cb ) around after the walk. So the garbage collector should succesfully collect all the garbage created.

You might however have high memory usage during the execution if the number of iterations are really high.

[ Amatuer article I wrote on this some time back ]
http://stefan.artspace44.com/javascript/memory-leaks/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文