GitHub版本号: https://github.com/cncounter/translation/blob/master/tiemao_2014/NodeList/NodeList.md
副标题: 为何getElementsByTagName()比querySelectorAll()快100倍
昨天,我在雅虎的同事 Scott Schiller (斯科特·席勒, 同一时候也是SoundManager创造者) 发Twitter询问为何getElementsByTagName("a")
在全部浏览器上都比 querySelectorAll("a")
要快好多倍。 有一个 专门的 JSPerf測试页面, 通过对照就能发现两者的速度差异相当明显。 比方作者在Windows XP下使用的 Firefox 3.6.8 浏览器,querySelectorAll("a")
比 getElementsByTagName("a")
的运行速度要低98%. 我和 Scott, 以及 YUI团队的 Ryan Grove 有一个活跃的Twitter-sation, 关于这样的现象的原因,以及情理之中让人沮丧的结果。 我想好好地解释说明下究竟为什么会发生这样的情况,以及为什么未来也可能不会改变。
在深入细节之前须要了解这两个方法间一个非常重要的差别,我想说的并非他们接收的參数一个是标签名,还有一个是整个CSS选择器。
而其最大的差别在于返回值的不同: getElementsByTagName()
方法返回一个动态的(live) NodeList
, 而querySelectorAll()
返回的是一个静态的(static) NodeList
. 理解这一点是非常必要的.
动态 NodeList
这是文档对象模型(DOM,Document Object Model)中的一个大坑. NodeList
对象(以及 HTML DOM 中的 HTMLCollection
对象)是一种特殊类型的对象. DOM Level 3 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()
。
原文链接: Why is getElementsByTagName() faster than querySelectorAll()?
原文日期: 2010-09-28
翻译日期: 2014-11-13
标签: getElementsByTagName, JavaScript, NodeList, querySelectorAll
DOM中的动态NodeList与静态NodeList的更多相关文章
-
Linux中的动态库和静态库(.a/.la/.so/.o)
Linux中的动态库和静态库(.a/.la/.so/.o) Linux中的动态库和静态库(.a/.la/.so/.o) C/C++程序编译的过程 .o文件(目标文件) 创建atoi.o 使用atoi. ...
-
JSP中的动态包含和静态包含的区别
本文转载自http://blog.csdn.net/xuxu198899223/article/details/8501044 1. 语法格式 (1)静态包含:<%@ include file= ...
-
Android笔记(二十七) Android中的动态广播和静态广播
广播接收器注册一共有两种形式 : 静态注册和动态注册. 两者及其接收广播的区别: 1.动态注册的广播 永远要快于 静态注册的广播,不管静态注册的优先级设置的多高,不管动态注册的优先级有多低>\ ...
-
在Cocos2D中改变动态物体为静态物体
原文链接,有压缩和简化 1.导入一个新的头文件 首先你要知道,不是所有Chimpunk特性都通过Cocos2d的类暴露出来,比如CCPhysicsNode和CCPhysicsBody.对于一些更高级的 ...
-
ARP缓存记录种类动态条目和静态条目
ARP缓存记录种类动态条目和静态条目 为使广播量最小,ARP维护IP地址到MAC地址映射的缓存以便将来使用.根据缓存的有效期时间,ARP缓存中包含动态和静态条目本文选自ARP协议全面实战手册. 这里首 ...
-
[转]JSP页面的动态包含和静态包含示例及介绍
原文地址:http://www.jb51.net/article/53659.htm 一.静态包含 本文介绍JSP静态包含语句,即使用JSP的include指令来完成的包含操作.JSP中,有两种包含其 ...
-
深入理解javascript中的动态集合——NodeList、HTMLCollection和NamedNodeMap
× 目录 [1]NodeList [2]HTMLCollection [3]NamedNodeMap[4]注意事项 前面的话 一说起动态集合,多数人可能都有所了解.但是,如果再深入些,有哪些动态集合, ...
-
DOM中的NodeList与HTMLCollection
最近在看<Javascript高级程序设计>的时候,看到了这样一句话:“理解NodeList和HTMLCollection,是从整体上透彻理解DOM的关键所在.”,所以觉得应该写一篇关于N ...
-
ios 开发中 动态库 与静态库的区别
使用静态库的好处 1,模块化,分工合作 2,避免少量改动经常导致大量的重复编译连接 3,也可以重用,注意不是共享使用 动态库使用有如下好处: 1使用动态库,可以将最终可执行文件体积缩小 2使用动态库, ...
随机推荐
-
X264库直接压缩BITMAP格式数据
最近帮朋友看了下X264压缩视频,主要参考了雷霄骅(leixiaohua1020)的专栏的开源代码: http://blog.csdn.net/leixiaohua1020/article/detai ...
-
hive中的常用方法(case,cast,unix_timestamp)
1.case的用法 )格式1 case col when value then '' when value then '' else '' end )格式2 case when col='value' ...
-
eclipse的debug模式启动缓慢
这个问题可能是由于eclipse和服务器的交互而产生的,在以debug模式启动服务器时,发生了读取文件错误,eclipse自动设置了断点,导致服务器不能正常启动. 解决方法如下:以debug模式启 ...
-
PHP函数参数的引用传递和值传递
函数的参数传递有两种方式 1,值传递 常见的 test($param) 方式就是值传递,在函数内部修改$param,不会影响外部变量$param的值 2,引用传递 参数是引用传递的方式,此时函数内部 ...
-
腾讯发布 Omix 1.0 - 用 JSX 或 hyperscript 创建用户界面
腾讯发布 Omix 1.0 - 用 JSX 或 hyperscript 创建用户界面 今天,腾讯正式开源发布 Omix 1.0, 让开发者使用 JSX 或 hyperscript 创建用户界面. Gi ...
-
POJ-2993 Emag eht htiw Em Pleh---棋盘模拟
题目链接: https://vjudge.net/problem/POJ-2993 题目大意: 输入和输出和这里相反. 思路: 模拟题,没啥算法,直接模拟,不过为了代码精简,还是花了一点心思的 #in ...
-
ArcGIS按选定线分割面-案例教程
ArcGIS按选定线分割面-案例教程 联系方式:谢老师,135-4855-4328,xiexiaokui#qq.com 功能 方法:高级编辑 实例: 分割前后 联系方式:谢老师,135-4855-43 ...
-
对于rqy今天讲座的一些理解和看法吧
其实我本来以为今天晚上要学高数的,但是听到任大佬要来讲课,我自然是很开心. 其实真正接触到他和照片给我的感觉完全不一样,rqy是一个非常单一的,没有在意其他过多的事情的人,包括从他的讲座来看,大佬把自 ...
-
Java项目中的异常处理情况
1. java.lang.nullpointerexception这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象 ...
-
ASP.NET Core开源地址
https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet 这个下面是asp.net core 框架 ...