高性能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 相关文章推荐
B/S开发中常用javaScript技术与代码
Mar 09 Javascript
jQuery学习笔记之jQuery的DOM操作
Dec 22 Javascript
Ajax请求在数据量大的时候出现超时的解决方法
Feb 27 Javascript
简介JavaScript中的setHours()方法的使用
Jun 11 Javascript
jQuery满屏焦点图左右滚动特效代码分享
Sep 07 Javascript
JavaScript使用DeviceOne开发实战(一) 配置和起步
Dec 01 Javascript
js仿支付宝多方框输入支付密码效果
Sep 27 Javascript
关于JS与jQuery中的文档加载问题
Aug 22 jQuery
React-Router如何进行页面权限管理的方法
Dec 06 Javascript
js中let和var定义变量的区别
Feb 08 Javascript
React为 Vue 引入容器组件和展示组件的教程详解
May 03 Javascript
jQuery实现轮播图效果
Nov 26 jQuery
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生成局部唯一识别码LUID的代码
2012/10/06 PHP
ThinkPHP框架搭建及常见问题(XAMPP安装失败、Apache/MySQL启动失败)
2016/04/15 PHP
php网页版聊天软件实现代码
2016/08/12 PHP
windows7配置Nginx+php+mysql的详细教程
2016/09/04 PHP
laravel 解决多库下的DB::transaction()事务失效问题
2019/10/21 PHP
jquery键盘事件使用介绍
2011/11/01 Javascript
javascript获取元素CSS样式代码示例
2013/11/28 Javascript
JS正则表达式验证数字代码
2014/01/28 Javascript
jquery文本框中的事件应用以输入邮箱为例
2014/05/06 Javascript
JavaScript数组对象实现增加一个返回随机元素的方法
2015/07/27 Javascript
js实现按钮颜色渐变动画效果
2015/08/20 Javascript
etmvc+jQuery EasyUI+combobox多值操作实现角色授权实例
2016/11/09 Javascript
clipboard.js在移动端复制失败的解决方法
2018/06/13 Javascript
Nuxt.js实现校验访问浏览器类型的中间件
2018/08/24 Javascript
vue.js 实现点击按钮动态添加li的方法
2018/09/07 Javascript
JS编写兼容IE6,7,8浏览器无缝自动轮播
2018/10/12 Javascript
详解Node.js 中使用 ECDSA 签名遇到的坑
2018/11/26 Javascript
vue全局使用axios的操作
2020/09/08 Javascript
vue3.0中setup使用(两种用法)
2020/12/02 Vue.js
[01:37]TI4西雅图DOTA2前线报道 VG拿下首胜教练357给出获胜秘诀
2014/07/10 DOTA
python使用smtplib模块通过gmail实现邮件发送的方法
2015/05/08 Python
Python导出数据到Excel可读取的CSV文件的方法
2015/05/12 Python
解决python3 urllib中urlopen报错的问题
2017/03/25 Python
python实现协同过滤推荐算法完整代码示例
2017/12/15 Python
Python + selenium自动化环境搭建的完整步骤
2018/05/19 Python
使用python实现语音文件的特征提取方法
2019/01/09 Python
对Python 检查文件名是否规范的实例详解
2019/06/10 Python
Python逐行读取文件内容的方法总结
2020/02/14 Python
唤醒头发毛囊的秘密武器:Grow Gorgeous
2016/08/28 全球购物
汽车维修专业毕业生的求职信分享
2013/12/04 职场文书
创建精神文明单位实施方案
2014/03/08 职场文书
剪彩仪式主持词
2014/03/19 职场文书
2014年安全生产大检查方案
2014/05/13 职场文书
罗马假日观后感
2015/06/08 职场文书
《珍珠鸟》教学反思
2016/02/16 职场文书
《围炉夜话》110句人生箴言,精辟有内涵,引人深思
2019/10/23 职场文书