为什么getElementsByTagName()要比querySelectorAll()快很多?

相信很多人第一反应当然是这样的了,querySelectorAll的参数是一个css selector,这一步还需要处理呢,肯定会比直接getElementsByTagName要慢了。具体jsperf上有相关的对比,确实getElementsByTagName要比querySelectorAll要快很多,这里不放具体链接了,因为这个网站挂了很长一段时间了已经。

废话不说,直接看原文Why is getElementsByTagName() faster than querySelectorAll()?

这里大概翻译一下,觉得不妥的地方还望指教。

 

首先这两个方法有个非常明显的区别,一个接受一个tag name的参数,另外一个接受一个css selector。这两个方法最大的不同点是,getElementsByTagName返回的是一个动态的NodeList而querySelectorAll返回的是一个静态的NodeList。

动态的NodeLists

NodeList和HTMLCollection是两个特别的对象类型,3级DOM中是这样定义HTMLCollection:

  NodeList和NamedNodeMap在DOM中都是动态的,也就是说,改变这些dom结构都会影响到这些NodeList和NamedNodeMap。例如:如果获取一个有子节点的NodeList,然后增加或者删除子节点,这个NodeList也会动态的改变。同样的,在一个tree结构中改变一个node会影响到所有引用这个node的NodeList和NamedNodeMap对象。

getElementsByTagName方法即返回的就是这样的一个NodeList,看下面代码

var divs = document.getElementsByTagName("div"),
    i=0;

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

这是一个死循环,因为divs.length在每次循环都会+1。

也许这样的动态设计是一个缺陷,但是开发者已经习惯了这种动态的NodeList。

 

静态的NodeLists

querySelectorAll返回的是一个静态的NodeList,下面是 Selectors API

  querySelectorAll返回的是一个静态的NodeList。改变一个相关node不会改变这个静态NodeList对象。也就是说这个对象必须在获取的时候就被创建出来。


所以即使querySelectorAll返回的结果和getElementsByTagName是一样的,但是他们还是有着本质的区别。在动态NodeList里return的是一个指针,而静态的NodeList返回时当前的node所有信息。

所以下面这个循环就不是一个死循环

var divs = document.querySelectorAll("div"),
    i=0;

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

因为在这段代码里,divs.length不会动态的改变。

 

所以为什么动态的NodeLists更快呢?

动态的NodeList返回的更快是因为不需要获取这个节点的所有信息,而静态的需要。为了证明这个观点,可以看一下webkit源码DynamicNodeList.cppStaticNodeList.cpp,这两种对象创建的方式不一样。

getElementsByTagName创建的过程不需要做任何操作,只需要返回一个指针即可(此处可以理解为shadow clone)。而querySelectorAll会循环遍历所有的的结果,然后创建一个新的NodeList(此处可以理解为deep clone)

 

结论

getElementsByTagName比querySelectorAll快的原因是因为动态和静态NodeList的区别。实际在用的过程中取决于你将要做什么,如果你只是查询一个tag name建议用getElementsByTagName,如果查询的是一个css selector建议使用querySelectorAll。

 

posted @ 2016-05-25 18:08  Tony_M  阅读(2813)  评论(2编辑  收藏  举报