高性能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 相关文章推荐
cookie丢失问题(认证失效) Authentication (用户验证信息)也会丢失
Jun 04 Javascript
JavaScript 处理Iframe自适应高度(同或不同域名下)
Mar 29 Javascript
jquery固定底网站底部菜单效果
Aug 13 Javascript
图片Slider 带左右按钮的js示例
Aug 30 Javascript
jQuery实现批量判断表单中文本框非空的方法(2种方法)
Dec 09 Javascript
详解javascript跨浏览器事件处理程序
Mar 27 Javascript
Bootstrap+jfinal退出系统弹出确认框的实现方法
May 30 Javascript
微信小程序 九宫格实例代码
Jan 21 Javascript
js编写简单的计时器功能
Jul 15 Javascript
Angular 5.x 学习笔记之Router(路由)应用
Apr 08 Javascript
vuex实现像调用模板方法一样调用Mutations方法
Nov 06 Javascript
vue el-upload上传文件的示例代码
Dec 21 Vue.js
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
各种咖啡的英文名子是什么
2021/03/03 新手入门
使用php+xslt在windows平台上
2006/10/09 PHP
浅析使用Turck-mmcache编译来加速、优化PHP代码
2013/06/20 PHP
php用户注册页面利用js进行表单验证具体实例
2013/10/17 PHP
详谈PHP面向对象中常用的关键字和魔术方法
2017/02/04 PHP
php在windows环境下获得cpu内存实时使用率(推荐)
2018/02/08 PHP
javascript 控制弹出窗口
2007/04/10 Javascript
javascript 日历提醒系统( 兼容所有浏览器 )
2009/04/07 Javascript
JS获取整个页面文档的实现代码
2011/12/15 Javascript
将查询条件的input、select清空
2014/01/14 Javascript
node.js入门教程
2014/06/01 Javascript
JS组件Bootstrap按钮组与下拉按钮详解
2016/05/10 Javascript
JavaScript简单拖拽效果(1)
2017/05/17 Javascript
浅谈Koa服务限流方法实践
2017/10/23 Javascript
深入理解Vue官方文档梳理之全局API
2017/11/22 Javascript
vue中将html字符串转换成html后遇到的问题小结
2018/12/10 Javascript
Node.js如何对SQLite的async/await封装详解
2019/02/14 Javascript
使用vue制作滑动标签
2019/09/21 Javascript
在vue中使用image-webpack-loader实例
2020/11/12 Javascript
[00:31]2016完美“圣”典风云人物:国士无双宣传片
2016/12/04 DOTA
python flask实现分页效果
2017/06/27 Python
Python sorted函数详解(高级篇)
2018/09/18 Python
python分布式编程实现过程解析
2019/11/08 Python
基于SpringBoot构造器注入循环依赖及解决方式
2020/04/26 Python
使用Pycharm在运行过程中,查看每个变量的操作(show variables)
2020/06/08 Python
css3实现背景模糊的三种方式(小结)
2020/05/15 HTML / CSS
跑步、骑行和铁人三项的高性能眼镜和服装:ROKA
2018/07/06 全球购物
英国复古和经典球衣网站:Vintage Football Shirts
2018/10/05 全球购物
印尼网上商店:Alfacart.com
2019/03/11 全球购物
高级销售员求职信
2013/10/25 职场文书
六一节目主持词
2014/04/01 职场文书
群众路线党员个人整改措施
2014/10/27 职场文书
活动总结模板大全
2015/05/11 职场文书
学者《孟子》名人名言
2019/08/09 职场文书
OpenCV-Python使用cv2实现傅里叶变换
2021/06/09 Python
MySQL之select、distinct、limit的使用
2021/11/11 MySQL