高性能JavaScript DOM编程(1)


Posted in Javascript onAugust 11, 2015

我们知道,DOM是用于操作XML和HTML文档的应用程序接口,用脚本进行DOM操作的代价很昂贵。有个贴切的比喻,把DOM和JavaScript(这里指ECMScript)各自想象为一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM,都要途径这座桥,并交纳“过桥费”,访问DOM的次数越多,费用也就越高。因此,推荐的做法是尽量减少过桥的次数,努力待在ECMAScript岛上。我们不可能不用DOM的接口,那么,怎样才能提高程序的效率?

1、DOM访问与修改
访问DOM元素是有代价的(“过桥费”你懂的),修改元素代价更是昂贵,因为它会导致浏览器重新计算页面的几何变化(重排和重绘)。

当然最坏的情况是在循环中访问或者修改元素,看下面两段代码:

var times = 15000;

// code1
console.time(1);
for(var i = 0; i < times; i++) {
 document.getElementById('myDiv1').innerHTML += 'a';
}
console.timeEnd(1);

// code2
console.time(2);
var str = '';
for(var i = 0; i < times; i++) {
 str += 'a';
}
document.getElementById('myDiv2').innerHTML = str;
console.timeEnd(2);

结果第一次运行的时间居然是第二次的千倍!(chrome 版本 44.0.2403.130 m)

1: 2846.700ms
2: 1.046ms

第一段代码的问题在于,每次循环迭代,该元素都会被访问两次:一次读取innerHTML的值,另一次重写它,也就是说,每次循环都在过桥(重排和重绘将在下一篇讲解)!结果充分表明,访问DOM的次数越多,代码的运行速度越慢。因此,能减少DOM访问的次数则尽量减少,尽量留在ECMAScript这端处理。

2、HTML集合 & 遍历DOM
操作DOM另一个耗能点就是遍历DOM,一般我们会收集一个HTML集合,比如用getElementsByTagName(),或者用document.links等,我想大家对此都不陌生。收集的结果是一个类似数组的集合,它处于一种“实时状态”实时存在,这意味着当底层文档对象更新时,它也会自动更新。怎么讲?很简单举个栗子:

<body>
 <ul id='fruit'>
 <li> apple </li>
 <li> orange </li>
 <li> banana </li>
 </ul>
</body>
<script type="text/javascript">
 var lis = document.getElementsByTagName('li');
 var peach = document.createElement('li');
 peach.innerHTML = 'peach';
 document.getElementById('fruit').appendChild(peach);

 console.log(lis.length); // 4
</script>

而这正是低效之源!很简单,跟数组的优化操作一样,缓存个length变量就ok了(读取一个集合的length比读取一个普通数组的lengh要慢很多,因为每次都要查询):

console.time(0);
var lis0 = document.getElementsByTagName('li');
var str0 = '';
for(var i = 0; i < lis0.length; i++) {
 str0 += lis0[i].innerHTML;
}
console.timeEnd(0);


console.time(1);
var lis1 = document.getElementsByTagName('li');
var str1 = '';
for(var i = 0, len = lis1.length; i < len; i++) {
 str1 += lis1[i].innerHTML;
}
console.timeEnd(1);

我们看看性能提升能有多少?

0: 0.974ms
1: 0.664ms

当集合的长度大的时候(demo是1000),性能提升还是很明显的。

而《高性能JavaScript》提出了另一个优化策略,它指出,“由于遍历数组比遍历集合快,因此如果先将集合元素拷贝到数组中,那么访问它的属性会更快”,经过测试,并没有很好地发现这个规律,所以还是不要多此一举了,测试代码如下:(有疑义欢迎与我交流探讨)

console.time(1);
var lis1 = document.getElementsByTagName('li');
var str1 = '';
for(var i = 0, len = lis1.length; i < len; i++) {
 str1 += lis1[i].innerHTML;
}
console.timeEnd(1);


console.time(2);
var lis2 = document.getElementsByTagName('li');
var a = [];
for(var i = 0, len = lis2.length; i < len; i++)
 a[i] = lis2[i];

var str2 = '';
for(var i = 0, len = a.length; i < len; i++) {
 str2 += a[i].innerHTML;
}
console.timeEnd(2);

本节的最后介绍两个原生DOM方法,querySelector()和querySelectorAll(),相信大家都不陌生,前者返回一个数组(注意,它们的返回值不像HTML集合一样会动态变化),后者返回匹配的第一个元素。好吧,其实并不是所有时候它的性能都优于前者的HTML集合遍历。

console.time(1);
var lis1 = document.getElementsByTagName('li');
console.timeEnd(1);

console.time(2);
var lis2 = document.querySelectorAll('li');
console.timeEnd(2);

// 1: 0.038ms
// 2: 3.957ms

但是因为它是类似CSS的选择方法,所以在做组合选择的时候,效率会提升,又方便。比如做如下的组合查询:

var elements = document.querySelectorAll('#menu a');
var elements = document.querySelectorAll('div.warning, div.notice');

以上就是关于高性能JavaScript DOM编程的全部内容,希望大家可以理解,对大家的学习有所帮助。

Javascript 相关文章推荐
用javascript实现的激活输入框后隐藏初始内容
Jun 29 Javascript
Jquery 高亮显示文本中重要的关键字
Dec 24 Javascript
使用jquery的ajax需要注意的地方dataType的设置
Aug 12 Javascript
js showModalDialog弹出窗口实例详解
Jan 07 Javascript
js+csss实现的一个带复选框的下拉框
Sep 29 Javascript
JS实现弹出浮动窗口(支持鼠标拖动和关闭)实例详解
Aug 06 Javascript
Bootstrap的class样式小结
Dec 01 Javascript
jQuery源码解读之extend()与工具方法、实例方法详解
Mar 30 jQuery
js实现点击切换checkbox背景图片的简单实例
May 08 Javascript
关于express与koa的使用对比详解
Jan 25 Javascript
使用Node.js实现base64和png文件相互转换的方法
Mar 11 Javascript
Vue列表循环从指定下标开始的多种解决方案
Apr 08 Javascript
jQuery中prepend()方法使用详解
Aug 11 #Javascript
javascript实现数组中的内容随机输出
Aug 11 #Javascript
javascript弹出拖动窗口
Aug 11 #Javascript
jquery.fastLiveFilter.js实现输入自动过滤的方法
Aug 11 #Javascript
javascript实现五星评价代码(源码下载)
Aug 11 #Javascript
jQuery的remove()方法使用详解
Aug 11 #Javascript
jquery实现点击展开列表同时隐藏其他列表
Aug 10 #Javascript
You might like
PHP提取中文首字母
2008/04/09 PHP
php查找任何页面上的所有链接的方法
2013/12/03 PHP
yii2安装详细流程
2018/05/23 PHP
ThinkPHP中获取指定日期后工作日的具体日期方法
2018/10/14 PHP
laravel利用中间件防止未登录用户直接访问后台的方法
2019/09/30 PHP
TP3.2.3框架使用CKeditor编辑器在页面中上传图片的方法分析
2019/12/31 PHP
myEvent.js javascript跨浏览器事件框架
2011/10/24 Javascript
js 自定义个性下拉选择框示例
2013/08/20 Javascript
js获取时间(本周、本季度、本月..)
2013/11/22 Javascript
利用javascript数组长度循环数组内所有元素
2013/12/27 Javascript
图片翻转效果具体实现代码
2014/01/09 Javascript
jquery 新建的元素事件绑定问题解决方案
2014/06/12 Javascript
JS控制网页动态生成任意行列数表格的方法
2015/03/09 Javascript
jQuery插件jRumble实现网页元素抖动
2015/06/05 Javascript
javascript中setInterval的用法
2015/07/19 Javascript
BootStrap响应式导航条实例介绍
2016/05/06 Javascript
后端接收不到AngularJs中$http.post发送的数据原因分析及解决办法
2016/07/05 Javascript
Vue原理剖析 实现双向绑定MVVM
2017/05/03 Javascript
JS解析url查询参数的简单代码
2017/08/06 Javascript
ES6 javascript中Class类继承用法实例详解
2017/10/30 Javascript
js定时器实现倒计时效果
2017/11/05 Javascript
JavaScript创建对象的四种常用模式实例分析
2019/01/11 Javascript
vue服务端渲染操作简单入门实例分析
2019/08/28 Javascript
vant 解决tab切换插件标题样式自定义的问题
2020/11/13 Javascript
[51:17]VGJ.T vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
python自动化工具日志查询分析脚本代码实现
2013/11/26 Python
python中模块的__all__属性详解
2017/10/26 Python
python+pandas+时间、日期以及时间序列处理方法
2018/07/10 Python
python获取时间及时间格式转换问题实例代码详解
2018/12/06 Python
完美解决ARIMA模型中plot_acf画不出图的问题
2020/06/04 Python
什么是Python包的循环导入
2020/09/08 Python
用python-webdriver实现自动填表的示例代码
2021/01/13 Python
css3的@media属性实现页面响应式布局示例代码
2014/02/10 HTML / CSS
跑操口号
2014/06/12 职场文书
先进个人材料怎么写
2014/12/30 职场文书
简单实现一个手持弹幕功能+文字抖动特效
2021/03/31 HTML / CSS