高性能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 &amp; DHTML 实例编程(教程)DOM基础和基本API
Jun 02 Javascript
jQuery封装的获取Url中的Get参数示例
Nov 26 Javascript
Javascript 函数parseInt()转换时出现bug问题
May 20 Javascript
JavaScript动态创建link标签到head里的方法
Dec 22 Javascript
jQuery左右滚动支持图片放大缩略图图片轮播代码分享
Aug 26 Javascript
JavaScript统计网站访问次数的实现代码
Nov 18 Javascript
分享javascript计算时间差的示例代码
Mar 19 Javascript
vue元素实现动画过渡效果
Jul 01 Javascript
Angular4绑定html内容出现警告的处理方法
Nov 03 Javascript
webpack3里使用uglifyjs压缩js时打包报错的解决
Dec 13 Javascript
如何阻止小程序遮罩层下方图层滚动
Sep 05 Javascript
javascript实现贪吃蛇游戏(娱乐版)
Aug 17 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程序中使用Rust扩展的方法
2015/07/03 PHP
实例讲解PHP设计模式编程中的简单工厂模式
2016/02/29 PHP
指定js可访问其它域名的cookie的方法
2007/09/18 Javascript
Ext grid 添加右击菜单
2009/11/26 Javascript
JQuery.uploadify 上传文件插件的使用详解 for ASP.NET
2010/01/22 Javascript
跟我学习javascript的prototype,getPrototypeOf和__proto__
2015/11/17 Javascript
JavaScript获取IP获取的是IPV6 如何校验
2016/06/12 Javascript
纯js实现悬浮按钮组件
2016/12/17 Javascript
Angularjs在360兼容模式下取数据缓存问题的解决办法
2017/06/22 Javascript
使用vs code开发Nodejs程序的使用方法
2017/09/21 NodeJs
详解Vue组件实现tips的总结
2017/11/01 Javascript
vue.js 获取select中的value实例
2018/03/01 Javascript
基于 vue-skeleton-webpack-plugin 的骨架屏实战
2019/08/05 Javascript
浅谈微信小程序列表埋点曝光指南
2019/10/15 Javascript
vue结合el-upload实现腾讯云视频上传功能
2020/07/01 Javascript
JS实现点击掉落特效
2021/01/29 Javascript
[01:33:25]DOTA2-DPC中国联赛 正赛 Elephant vs IG BO3 第一场 1月24日
2021/03/11 DOTA
Tensorflow卷积神经网络实例进阶
2018/05/24 Python
在matplotlib的图中设置中文标签的方法
2018/12/13 Python
python使用response.read()接收json数据的实例
2018/12/19 Python
Python学习笔记之集合的概念和简单使用示例
2019/08/22 Python
Python django框架输入汉字,数字,字符生成二维码实现详解
2019/09/24 Python
python ImageDraw类实现几何图形的绘制与文字的绘制
2020/02/26 Python
Python如何用wx模块创建文本编辑器
2020/06/07 Python
HTML5 DeviceOrientation实现手机网站摇一摇功能代码实例
2015/04/24 HTML / CSS
美国从事品牌鞋类零售的连锁店:Famous Footwear
2016/08/25 全球购物
Koral官方网站:女性时尚运动服
2019/04/10 全球购物
Spotahome意大利:公寓和房间出租
2020/02/21 全球购物
文秘求职信范文
2014/04/10 职场文书
中华在我心中演讲稿
2014/09/13 职场文书
2015年体育部工作总结
2015/04/02 职场文书
电影地道战观后感
2015/06/04 职场文书
《纸船和风筝》教学反思
2016/02/18 职场文书
javascript canvas实现雨滴效果
2021/06/09 Javascript
vue实现省市区联动 element-china-area-data插件
2022/04/22 Vue.js
详解Android中的TimePickerView(时间选择器)的用法
2022/04/30 Java/Android