静态NodeList 和 动态NodeList的区别

ps:了解这个知识点的原因

前两天我在重温js dom编程的时候,看到了获取dom元素这一章,然后看到了getElementsByTagName()和getElementsByClassName(),之后又了解到了现代浏览器新出的一个DOM API--querySelectorAll().以我的性格,看到这些方法之后我肯定是想了解一下它们的不同点啦,所以我就翻阅资料,就看到了stackoverflow上面的一个问题

var temp = document.querySelectorAll(".class");
for (var i=0, max=temp.length; i<max; i++) { 
    temp[i].className = "new_class";
}

var temp = document.getElementsByClassName("class");
for (var i=0, max=temp.length; i<max; i++) { 
    temp[i].className = "new_class";
} 

运行上述这两段代码,假如获取到的temp的长度都为3,那么第一段代码能将三个元素的className全部更改为new_class",而第二段代码只能讲第一个元素和第三个元素的className更改为"new_class".这里面的原因就是动态nodelist和静态nodelist的区别。
然后我又翻阅资料查找什么是动态nodelist,什么是静态nodelist。于是乎,就有了下面的长篇大论。

说说NodeList,HTMLCollection以及NamedNodeMap

在不同版本的浏览器中,如果调用获取多元素的DOM方法(getElement...()),有的会得到NodeList(多为旧浏览器),有的会得到HTMLCollection(多为新浏览器)。使用Node Interface的方法,如childNodes,得到的通常是NodeList,而使用其他Interface的方法,又有可能得到HTMLCollection。而NamedNodeMap又和前面两者返回的东西类型也不相同,所以有必要了解一下这三者的区别。

1. 三者的相同点

1.1 三者都具有length属性

1.2 三者都有item()方法

1.3 三个集合都是"动态的",如果对NodeList和HTMLCollection中的元素进行操作都会直接反映到DOM中,因此如果一次性直接在集合中进行DOM操作的话,开销非常大。(这会在讲解动态的时候详细解释)

2. 三者的不同点

2.1 nodeList里面包含了所有的节点类型,比如元素节点,文本节点等

2.2 HTMLCollection里面只包含元素节点

<div>
    <!-- Comment -->
    <p>This is Some Text</p>
</div>

上面这段代码,如果作为NodeList返回,那么浏览器最多会给这个列表5个元素

1.一个<div>和注释间的断行和空格(或tab)作为text node(没错,标签之间的空白符号也可以被解析为text node

2.注释作为comment node

3.注释和<p>之间的断行和空格(或tab)作为text node,p作为element

4.</p></div>之间的断行和空格(或tab)作为text node

但是如果是作为HTMLCollection返回的话,那么就一个<p>元素这么简单

2.3 NamedNodeMap里面包含了"Attribute"的集合,例如id,title,name等,集合中的每一个元素都是attr类型。

2.4 三个集合所提供的方法也不相同,例如HTMLCollection中提供了namedItem(),而其它两个集合就没有提供这个方法

扩展点:

  1. item和namedItem都可以通过[]的缩写进行调用,有的浏览器还支持用()的缩写进行调用(也就是可以list[index],list[key]或者list(index),list(key)),以及直接用dot notation调用namedItem(比如list.key)

  2. IE8及以下版本浏览器中,注释属于HTMLCommentElement,算作Element,因此会出现在HTMLCollection里

  3. 我们可以用alert/console.log(document.getElement...)打印出来看下返回的是什么类型的集合,下面这个链接中讲的也算详细,可以参考下:http://www.jb51.net/article/25747.htm

ps:以上知识点参考链接:

http://www.cnblogs.com/joyeecheung/p/4067927.html,
http://stackoverflow.com/questions/15763358/difference-between-htmlcollection-nodelists-and-arrays-of-objects,
http://stackoverflow.com/questions/26047844/getelementsbyclassname-vs-queryselectorall

说了这么多,那么到底什么是动态NodeList?什么是静态NodeList呢?它们之间有什么区别?

动态NodeList

上面我们说到NodeList,HTMLCollection以及NamedNodeMap都是动态的。也就是说,对底层文档结构的修改会动态地反映到相关的结合NodeList,HTMLCollection以及NamedNodeMap中。例如:如果先获取了某个元素的子元素的动态集合NodeList对象,然后又在其他地方对这个元素进行操作(添加,修改,删除子元素等操作),这些更改将自动反射到NodeList中,不需要手动进行操作。

因为getElementsByTagName(所有getElement...方法都会返回动态NodeList)方法返回的是一个动态集合,所以只要document发生变化,就会自动更新对应的元素。因此,下面的代码是一个死循环:

var divs = document.getElementsByTagName("div");
var i=0;
while(i < divs.length){
  document.body.appendChild(document.createElement("div"));
  i++;
}

死循环的原因是每次循环都会重新计算divs.length.每次迭代都会添加一个新的<div>,所以每次i++,对应的divs.length也在增加,所以i永远比divs.length小,循环终止条件也就永远不会触发。

解决上述代码死循环的办法可以是用一个变量存储divs.length或者改用querySelectorAll():

var divs = document.getElementsByTagName("div");
var i=0,len = divs.length;
while(i < len){
  document.body.appendChild(document.createElement("div"));
  i++;
}

你可能会觉得这种动态集合是个坏主意, 但通过动态集合可以保证某些使用非常普遍的对象在各种情况下都是同一个,而且动态NodeList比静态NodeList快很多很多(下面解释原因)

静态NodeList

querySelectorAll()和querySelector()方法返回的是一个静态的NodeList,所谓静态NodeList就是对底层document的更改不会影响到返回的这个NodeList对象.此时返回的NodeList只是querySelectorAll()方法被调用时的文档状态的快照。所以下面的代码不会是死循环:

var divs = document.querySelectorAll("div");
var i=0;
while(i < divs.length){
    document.body.appendChild(document.createElement("div"));
    i++;
}

在这种情况下没有死循环, divs.length的值永远不会改变, 所以只要不满足循环条件, 就退出循环。

为什么动态NodeList更快呢?

我在某篇文章中看到有人测试了一下getElementsByTagName()比querySelectorAll()快好多倍。

原因是:动态NodeList对象在浏览器中可以更快地被创建并返回,因为他们不需要预先获取所有的信息,而静态NodeList对象从一开始就需要取得并封装所有相关的数据。两种对象类型的创建方式是完全不同的。

DynamicNodeList(动态NodeList)对象通过在cache缓存中注册它的存在并创建。从本质上讲,创建一个新的DynamicNodeList是非常轻量级的,因为不需要做任何的前期工作。每次访问 DynamicNodeList 时, 必须查询 document 的变化, length 属性 以及 item() 方法证明了这一点

相比之下,StaticNodeList对象实例由另外一个文件创建,然后循环填充所有的数据。在document中执行静态查询的前期成本相比DynamicNodeList要显著提高很多倍。

如果真正的查看WebKit的源码,你会发现他为 querySelectorAll() 明确地 创建一个返回对象 ,在其中又使用一个循环来获取每一个结果,并创建最终返回的一个 NodeList.

结论

getElementsTagName()方法速度比querySelectorAll()方法快的根本原因在于动态NodeList和静态NodeList对象不同。在获取NodeList时不需要执行很多前期处理操作的动态列表总比获取返回之前完成各种处理的静态NodeList要快很多。哪个方法更好用还是看你的需求。如果不需要获取快照,就使用getElement...方法;如果需要静态快照结果,或者需要使用更复杂的css查询,则可以考虑querySelectAll()方法

对开头题目讲解

/**通过querySelectorAll()获取到的元素集合temp是静态的快照,所以temp长度不会变化,max始终为3;所以通过for循环3个对应元素的class名字都被改成"new_class"**/
var temp = document.querySelectorAll(".class");
for (var i=0, max=temp.length; i<max; i++) { 
     temp[i].className = "new_class";
}
/**通过getElementsByClassName()获取到的元素集合temp是动态的,所以我们对元素任何的更改都会直接反映到对应的NodeList中;
刚开始temp长度为3,也就是max为3,这里i=0的时候,更改了temp[0]的className为"new_class",所以temp的长度马上发生变化,max变为2;
继续循环,i=1的时候,temp[1]实际上是没变化前的temp[2]。此时又更改了temp[1]的className为"new_class",所以temp的长度又发生变化,max变为1;
继续循环,i=2的时候,不满足条件,循环结束;
所以temp[0],temp[2]的className都变为"new_class",而temp[1]没改变**/
var temp = document.getElementsByClassName("class");
for (var i=0, max=temp.length; i<max; i++) { 
     temp[i].className = "new_class";
} 

参考资料链接:

https://github.com/cncounter/translation/blob/master/tiemao_2014/NodeList/NodeList.md

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容