遍历NodeList对象时的性能优化
看到《JavaScript中高级编程》中提到,对于getElementsByClassName()之类方法返回的Nodelist对象,“应该尽量减少访问NodeList的次数,因为每次访问NodeList,都会运行一次基于文档的查询。”
针对这个问题,解决办法就是在遍历之前先将NodeList复制一份,对这个复制节点进行遍历。这个办法是否有效,能提高多少效率,我做了一个测试
测试代码在最后,先看结果(每行都进行了5次测试):
- 100个同名class时,直接遍历用时0,0,0,0,0ms,复制后遍历用时0,0,0,0,0ms
- 1000个同名class时,直接遍历用时0,1,0,1,1ms,复制后遍历用时1,1,1,2,1ms
- 1000个同名class时,直接遍历用时4,3,2,2,2ms,复制后遍历用时6,10,10,10,7ms
- 10000个同名class时,直接遍历用时5,2,5,1,3ms,复制后遍历用时13,11,10,6,12ms
这么看来,复制后遍历好像并不如直接遍历来的快,反而导致性能变差了,造成这种现象原因应该是复制NodeList的时间大于了每次更新NodeList的时间和。
那么我猜想是不是因为本例结构树过于单一,在实际应用里更新NodeList应该会比较慢一些。那么我就拿我的网站来做个测试吧。
代码不变,很遗憾的发现,结果和上面的并没有什么两样,打开performance录了一下,一看发现确实是复制节点占用了大量时间
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="content"></div>
<script type="text/javascript">
var content = document.getElementById('content');
var testNum = 100000;
for (var i = 0; i < testNum; i++) {
var a = document.createElement('div');
a.setAttribute('class','a')
content.appendChild(a);
}
function time(func, id){
var start = Date.now();
func(id);
var stop = Date.now();
return stop - start;
}
function test1(ele){
var nodeList = ele.getElementsByClassName('a');
for (var i = 0; i < nodeList.length; i++) {
var a = nodeList[i];
}
}
function test2(ele){
var nodeListCopy = ele.cloneNode(true);
var nodeList = nodeListCopy.getElementsByClassName('a');
for (var i = 0; i < nodeListCopy.length; i++) {
var b = nodeListCopy[i];
}
}
console.log(time(test1,content))
console.log(time(test2,content))
</script>
</body>
</html>