I have a Django admin page for a nested category list like this:
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>
{% endbl开发者_如何学JAVAock %}
...the script runs fine and the list looks like this:
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?
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).
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/
精彩评论