我有一个嵌套类别列表的 Django 管理页面,如下所示:
我写了这个脚本来对列表进行排序并分层呈现:
{% 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 %}
...脚本运行良好,列表如下所示:
我的问题是:我觉得脚本在垃圾回收较弱的 UA 中容易出现内存泄漏,因为父/子内容创建的循环引用。这是我应该担心的事情吗?有没有更好的方法来编写脚本?我应该在脚本末尾删除一堆东西吗?如果是,怎么办?
最佳答案
我看到一些小漏洞,IE 垃圾收集器在清理函数内的事件节点引用时出现问题。这是因为 DOM 和 JavaScript 都有自己的垃圾收集器,基本上,它们不想为了引用而互相争吵。
既然你每页调用一次这个脚本?内存泄漏很小,实际上可以忽略,除非人们在一次 session 中打开 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 元素无效(当然,一旦您完成了它们)。
关于javascript - 防止此 javascript 代码中的内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3645386/