点进来你就知道,98%程序员都不清楚的小秘密:为何getElementsByTagName()比querySelectorAll()快100倍

getElementsByTagName()和querySelectorAll()最大的区别在于返回值的不同:

getElementsByTagName() 方法返回一个动态的(live) NodeList, 而querySelectorAll() 返回的是一个静态的(static) NodeList. 理解这一点是非常必要的.

动态NodeList

这是文档对象模型(DOM,Document Object Model)中的一个大坑. NodeList 对象(以及 HTML DOM 中的 HTMLCollection对象)是一种特殊类型的对象.  DOM Level3 spec规范对 HTMLCollection 对象的描述如下:

DOM中的 NodeList 和 NamedNodeMap 对象是动态的(live); 也就是说,对底层文档结构的修改会动态地反映到相关的集合NodeList 和 NamedNodeMap 中。 例如, 如果先获取了某个元素(Element)的子元素的动态集合 NodeList 对象, 然后又在其他地方顺序添加更多子元素到这个DOM父元素中( 可以说添加, 修改, 删除子元素等操作), 这些更改将自动反射到NodeList, 不需要手动进行其他调用. 同样地, 对DOM树上某个Node节点的修改,也会实时影响引用了该节点的 NodeList和 NamedNodeMap 对象。

getElementsByTagName() 方法返回对应标签名的元素的一个动态集合, 只要document发生变化,就会自动更新对应的元素。 因此, 下面的代码实际上是一个死循环:

// XXX 实际中请注意...
// 适当的中间变量是一个好习惯
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小, 循环终止条件也就不会触发[例外情况是dom中没有div,不进入循环]。

你可能会觉得这种动态集合是个坏主意, 但通过动态集合可以保证某些使用非常普遍的对象在各种情况下都是同一个, 如document.images , document.forms, 以及其他类似的 pre-DOM集合。

静态NodeList

querySelectorAll() 方法的不同是它返回一个静态的 NodeList. 这是表示的 选择器API规范 :

querySelectorAll() 方法返回的 NodeList 对象必须是静态的, 而不能是动态的([DOM-LEVEL-3-CORE], section 1.1.1). 后续对底层document的更改不能影响到返回的这个 NodeList 对象. 这意味着返回的对象将包含在创建列表那一刻匹配的所有元素节点。

所以即便是让 querySelectorAll() 和 getElementsByTagName() 具有相同的参数和行为, 他们也是有很大的不同点。 在前一种情况下, 返回的 NodeList 就是方法被调用时刻的文档状态的快照, 而后者总是会随时根据document的状态而更新。 下面的代码就不会是死循环:

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

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

在这种情况下没有死循环, divs.length的值永远不会改变, 所以循环实际上就是将 <div> 元素的数量增加一倍, 然后就退出循环

为什么动态NodeList更快呢?

动态 NodeList 对象在浏览器中可以更快地被创建并返回,因为他们不需要预先获取所有的信息, 而静态 NodeList 从一开始就需要取得并封装所有相关数据. 再三强调要彻底了解这一点, WebKit 的源码中对每种 NodeList 类型都有一个单独的源文件: DynamicNodeList.cpp 和 StaticNodeList.cpp. 两种对象类型的创建方式是完全不同的。

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

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

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

结论

getElementsByTagName() 速度比 querySelectorAll() 快的根本原因在于动态NodeList和静态NodeList对象的不同。 尽管我可以肯定地说有某种方法来优化这一点, 在获取NodeList时不需要执行很多前期处理操作的动态列表,总比获取静态的集合(返回之前完成各种处理)要快很多。 哪个方法更好用主要还是看你的需求, 如果只是要根据 tag name 来查找元素, 也不需要获取此一个快照, 那就应该使用 getElementsByTagName()方法; 如果需要快照结果(静态),或者需要使用复杂的CSS查询, 则可以考虑 querySelectorAll()

时间: 2024-11-08 12:26:09

点进来你就知道,98%程序员都不清楚的小秘密:为何getElementsByTagName()比querySelectorAll()快100倍的相关文章

每个程序员都应该了解的 CPU 高速缓存

每个程序员都应该了解的 CPU 高速缓存 英文原文:Memory part 2: CPU caches 来源:oschina [编者按:这是Ulrich Drepper写“程序员都该知道存储器”的第二部.那些没有读过第一部 的读者可能希望从这一部开始.这本书写的非常好,并且感谢Ulrich授权我们出版. 一点说明:书籍出版时可能会有一些印刷错误,如果你发现,并且想让它在后续的出版中更正,请将意见发邮件到[email protected] ,我们一定会更正,并反馈给Ulrich的文档副本,别的读者

后端程序员都做些什么?

后端程序员都做些什么? 2017-12-25 刘欣 程序猿 来自:码农翻身(微信号:coderising) 这个问题来自于QQ网友,一句两句说不清楚,索性写个文章. 我刚开始做Web开发的时候,根本没有前端,后端之说. 原因很简单,那个时候服务器端的代码就是一切:接受浏览器的请求,实现业务逻辑,访问数据库,用JSP生成HTML,然后发送给浏览器. 即使后来Javascript在浏览器中添加了一些AJAX的效果,那也是锦上添花,绝对不敢造次.因为页面的HTML主要还是用所谓"套模板"的方

程序员都不知道的5种将死的编程语言

现在的开发人员都趋向于使用新的编程语言,那么旧的编程语言呢?它们的前途一般是这样两种:仍然可以使用,但逐渐不受大家欢迎,直接完全死去.程序员都不知道的5种将死的编程语言下面给大家说说: Perl 曾几何时,几乎每个人都在使用Perl语言编程.但是那些经常使用的人慢慢地发现,关于这个Perl语言似乎总是有点不对劲.至少我知道有这么个叫做"piecemeal"的编程语言,它的创造者似乎就只是将这个功能堆在另一个功能上面而已,并没有好好考虑将它们结合在一起.事实上,甚至是它的创造者也不得不承

StackOverflow程序员推荐:每个程序员都应读的30本书

“如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本,你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西.” 很多程序员响应,他们在推荐时也写下自己的评语.以前就有国内网友介绍这个程序员书单,不过都是推荐数 Top 10的书.其实除了前10本之外,推荐数前30左右的书籍都算经典,伯乐在线整理编译这个问答贴,同时摘译部分推荐人的评语.下面就按照各本书的推荐数排列. 1. <代码大全>史蒂夫·迈克康奈尔 推荐数:1684 “优秀的编程实践的百科全书,&l

程序员都是有强迫症的

昨天晚上,为了完成实验室任务,一直写代码,写到两点多,然后总算是写完了.但是程序员都知道,调试的过程通常是最复杂的,因为可能遇到各种奇葩的错误,而错误产生的原因多种多样,或者是逻辑错误,或者是输入错误,或者是访问错误...各种各样的错误,毫无头绪..... 程序员大都有强迫症,尤其在编程这件事情上.为了改正程序中的错误,可以熬夜,直到找到错误并改正错误. 找到错误本身就有挑战,因为有些错误是逻辑上的错误,这种错误通常不易发现,只是结果和预期结果不一样,这时候就需要从头去思考整个流程,判断每一步是

每个程序员都可能犯过的10个错误

本文列出的10个错误,并不局限于C#.Java.Delphi.JavaScript等——几乎涵盖了所有的编程语言.是不是大吹大擂,欢迎各位品鉴…… 1.面向编译器写代码,而不是面向用户 当人们使用编译器创建自己的App时,在把自己的想法诉诸于机器代码的过程中,常常会将那些可以使得编程更为简单却又冗长的语法遗忘于脑后.无论你使用的是单字母的标识符还是更易于人脑理解的标识符,对于编译器而言,毫无区别.编译器不在乎你写的是否是优化表达式,也不在乎你是否用括号封装了子表达式.编译器要做的就是将这些人脑可

每个程序员都应该了解的内存知识

每个程序员都应该了解的内存知识 英文原文:lwn.net,翻译:开源中国 [编辑的话: Ulrich Drepper最近问我们,是不是有兴趣发表一篇他写的内存方面的长文.我们不用看太多就已经知道,LWN的读者们会喜欢这篇文章的.内存的使用常常是软件性能的决定性因子,而如何避免内存瓶颈的好文章却不好找.这篇文章应该会有所帮助. 他的原文很长,超过100页.我们把它分成了7篇,每隔一到两周发表一篇.7篇发完后,Ulrich会把全文发出来. 对原文重新格式化是个很有挑战性的工作,但愿结果会不错吧.为了

转:哪本书是对程序员最有影响、每个程序员都该阅读的书?

哪本书是对程序员最有影响.每个程序员都该阅读的书? 国外知名网站stackoverflow上有一个问题调查: 哪本书是对程序员最有影响.每个程序员都该阅读的书?,这个调查已历时两年,目前为止吸引了153,432人访问,读者共推荐出了478本书(还在增加),其中最火的一本书<Code Complete>被顶了1306次.如果你是个程序员,你一定有兴趣看看这些书里你都看过几本,如果你一本没看过的话,我也不好说什么,也许你是个天才,但我相信大多数人都知道,你在学校里根本学不到什么真正的工作中需要的知

国外程序员推荐:每个程序员都应该读的非编程书

1. <银河系漫游指南>by Douglas Adams 2. <人性的弱点> by Dale Carnegie 3. <别逗了,费曼先生> 4. <一九八四> by George Orwell 5. <哥德尔.艾舍尔.巴赫:集异璧之大成> by Douglas Hofstadter 6. <设计心理学> by Donald A. Norman 7. <搞定:无压工作的艺术>by David Allen 8. <人月