第一次读《JavaScript高级程序设计(第三版)》,没怎么注意和理解JavaScript 动态集合的概念,最近二次阅读这本书,想进一步对其做深入的研究。现于此细究一下这三个动态集合(HTMLCollection、NodeList、NodeNameMap)之间的用法和联系及区别。
这里的动态集合是指:DOM结构的变化能够自动反应到所保存的对象中。
NodeList
NodeList 是一种类数组对象,是node节点(12种)的集合,用于保存一组有序的节点,可以通过节点的位置访问这些节点。在childNodes属性和querySelectorAll()方法返回值中保存着 NodeList 对象。
在childNodes属性中的NodeList对象
var div = document.getElementById("div1");
var children = div.childNodes; //获取div元素子节点集合
alert(children instanceof NodeList); //true
通过querySelectAll()方法返回值中的NodeList对象
var divs = documene,qrerySelectAll('div');
alert(divs.instance of NodeList) //true
这里需要注意的是通过querySelectorAll()方法返回值中保存着 NodeList 对象是静态集合;这里可以与下文中的HTMLCollection 对象作为对比,如下图所示:
如此就可以看出两者之间“动态”和“静态”的区别。
HTMLCollection
HTMLCollection对象与NodeList对象类似,都是节点的集合,返回的都是类数组对象。但他们也存在着不同之处:NodeList集合包含着node节点中12种节点,而HTMLCollection 仅包含elements 元素节点的集合。
HTMLCollection对象包含于getElemenstByTagName()、getElementsByClassName()、getElementsByName()等方法返回的值,以及children、document.links、document.forms等元素集合。
<div id="test"></div>
<script>
var childN = test.children;
//IE7-浏览器并未定义HTMLCollection对象,会报错,其他浏览器返回true
alert(childN instanceof HTMLCollection);
var tags =test.getElementsByTagName('div');
//IE7-浏览器并未定义HTMLCollection对象,会报错,其他浏览器返回true
alert(tags instanceof HTMLCollection);
</script>
NameNodeMap
这个对象包含于attributes 属性中,元素的每一个特性都由一个Attr 节点表示,每个节点都保存在 NameNodeMap 对象中。
var div = document.getElementById("div1");
var attrs = div.attributes; //获取div元素的特性
alert(children instanceof NamedNodeMap); //true
类数组转数组的方法
之前了解 arguments 对象的都知道,它是一个类数组对象,有数组的表达方式,但并没有数组方法。而HTMLCollection、NodeList、NodeNameMap 这三者同样与 arguments 对象一样。因此必须将其实现由类数组转化为数组:
function convertToArray(nodes) {
var array = null;
try {
array = nodes.prototype.slice.call(nodes, 0);
} catch {
//由于IE8-浏览器将NodeList实现为一个COM对象,不能使用Array.prototype.slice()方法,必须手动枚举所有成员。
array = new Array();
for(var i = 0, len = nodes.length; i <len; i++) {
array.push(nodes[i]);
}
}
}
var ff = convertToArray(nodes);
console.log(ff instanceof Array); //true
因DOM操作是往往是JavaScript 程序中开销最大的部分,因此在循环遍历这些动态集合时,不要忽略其动态性,从而避免穿死循环,如下两段代码对比:
var divs = document.getElementsByTagName("div");
for(var i = 0 ; i < divs.length; i++){
document.body.appendChild(document.createElement("div"));
}
在上面代码中,由于divs是一个HTMLElement集合,divs.length会随着appendChild()方法,而一直增加,于是变成一个死循环。
为了避免此种情况发生,代码改进如下:
var divs = document.getElementsByTagName("div");
for(var i = 0,len = divs.length; i < len; i++){
document.body.appendChild(document.createElement("div"));
}
一般地,要尽量减少访问NodeList、HTMLCollection、NamedNodeMap的次数。因为每次访问它们,都会运行一次基于文档的查询。所以,可以考虑将它们的值缓存起来。
对于JavaScript 的动态集合就总结这么多,若有纰漏方请指正,后期后跟进补充其它的JavaScript 内容。