2010-09-05 85 views
2

我有一个嵌套的类别列表这样的Django管理页面: alt text防止此JavaScript代码中的内存泄漏?

我写这个剧本对列表进行排序和层级形式呈现它:

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

我的问题是:我觉得脚本很容易出现内存泄漏UAs的弱垃圾收集,因为由父/子创建的循环引用st UFF。这是我应该担心的吗?有没有更好的方法来编写脚本?我应该在脚本末尾删除一堆东西,如果是这样,什么?

+1

你“感觉”脚本容易发生内存泄漏,或者你观察到内存泄漏? – 2010-09-05 07:56:55

+0

我还没有看到任何东西,但我觉得IE可能会做一些愚蠢的事情,比如在页面导航时不收集垃圾。不幸的是我没有办法测试那台ATM机。 – 2010-09-05 08:01:05

+0

你操作系统允许你观察随着时间的推移吗?如果是这样,我确定你可以设置一个原始测试,看看是否有不必要的内存泄漏... – Stefan 2010-09-05 08:25:04

回答

1

我看到一些轻微的泄漏,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元素(一旦你完成了它们的课程)。

+0

感谢您的回复。问题:是否可以将'r','catName','opt'和'rows'(所有你没有使用的变量)变成某个对象的属性,比如table.el,而不是将它们的引用归零?我很好奇为什么你为'table'和其他人采取了两种不同的方法。 – 2010-09-05 20:46:34

+0

前四个只用于一个循环,所以不明白为什么要将它们保存在一个对象上,通过传递给walk()的闭包,通过匿名函数重用该表。被称为walk的循环会在每次迭代中创建一个函数,因此可能会留下很多潜在的活动元素引用。我把它放在一个对象上,所以这个函数只知道JS对象引用,而对象保存了DOM元素引用。 – BGerrissen 2010-09-05 21:57:04

+0

事实上,你也可以在循环之前声明传递给walk()的函数,所以你不会在每次迭代中创建一个函数,但这是一个小的优化。 – BGerrissen 2010-09-05 21:58:50

1

脚本不似乎包含任何严重的内存泄露,因为它没有任何功能后步行各地(如CB)离开。所以垃圾收集器应该成功地收集所有创建的垃圾。

但是,如果迭代次数真的很高,您可能会在期间内存使用率很高

[业余文章中,我在这一段时间回信] http://stefan.artspace44.com/javascript/memory-leaks/