深入理解Javascript中的循环优化


Posted in Javascript onNovember 09, 2013

循环是大多数编程语言都具备的基本功能,JS也不例外,不同之处在于JS是解释型语言,运行于浏览器环境中,客户端的软硬件条件会对JS执行效率产生很大的影响。然而客户端环境对于开发者是未知、多样的,并且难以改变,所以优化代码质量是提高代码效率的主要途径。
JS代码中,循环是比较容易导致性能问题的因素。理解循环特性进而有针对性地进行优化也许会带来不错的性能提升。
for、while、do-while循环:
这三种循环本身的循环效率相差不多,所以只要根据适合的应用场景选择即可。
以for循环为例:

var aValues = ["a", "b", "c", "d"];
for(var i = 0; i < aValues.length; i += 1){
 fDoSomethingA(aValues[i]);
 fDoSomethingA(aValues[i]);
}

上面例子中每次循环都要比较i与数组的长度,所以每次都要重新读取数组长度,由如果数组长度在循环中是不变的,这样做就没有必要,我们可以使用局部变量代替length的读取。同理,例子中,aValues[i]由于被读取两次以上,我们也可以将它赋值给局部变量:
var aValues = ["a", "b", "c", "d"], nLength = aValues.length;
 for(var i = 0, sValue; i < nLength; i += 1){
 sValue = aValues[i];
 fDoSomethingA(sValue);
 fDoSomethingB(sValue);
 }

如果循环的业务逻辑对循环顺序不敏感,可以尝试倒序循环,即将计数器递减到0。
var aValues = ["a", "b", "c", "d"], nLength = aValues.length;
 for(var i = nLength, sValue; i -= 1;){
 sValue = aValues[i];
 fDoSomethingA(sValue);
 fDoSomethingB(sValue);
 }

使用这种方式计数器默认与0进行比较,连局部变量比较都省略了,理论上也能提高效率。
for-in循环:
for-in循环更像在穷举,他用来遍历对象属性,我们知道对象属性的查找会一直延续到原型链顶端,这将大大降低循环效率。for-in循环的写法上没有什么优化空间,需要在使用时遵循一定原则:尽量只在遍历数据型对象的时候才使用for-in循环。
如果遍历对象的属性是明确的,可以使用数组循环替代。
例如遍历一个联系人对象:
var aContact = ["N", "FN", "EMAIL;PREF", ...];
 for(var i = aContact.length; i -= 1;){
 fDoSomething(aContact[i]);
 }
 

Duff策略
Duff策略的主要原理是通过展开循环减少次数来提高效率。例如
一个普通循环:
for(var i = aValues.length; i -= 1){
 fDoSomething(aValues[i]);
 }
 

如果aValues.length == N,写成以下这种方式的效率将比循坏来的高:
fDoSomething(aValues[0]);
 fDoSomething(aValues[1]);
 fDoSomething(aValues[2]);
 fDoSomething(aValues[3]);
 ...
 ...
 fDoSomething(aValues[N-1]);

但如果N很大,这种写法就不现实,而Duff策略是一种适中的循环展开策略。
近日在网易邮箱通讯录联系人的初始化循环中加入了Duff策略:
var nLength = aContacts.length,
// 总轮数
 nRounds = Math.floor( nLength / 8),
// 额外余量
 nLeft = nLength % 8,
i = 0;
// 先处理余量
 if(nLeft){
 do{
 fFormat(aContacts[i ++]);
 }while(-- nLeft)
}
// 每轮执行8次格式化
 if(nRounds){
 do{
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 fFormat(aContacts[i ++]);
 }while(-- nRounds)
 }

如上所示,每轮循环可以执行8个联系人数据的格式化操作,还有一轮循环用于处理余下的联系人。由此可见,在联系人较多的情况下总的循环次数大大降低,可以降低循环的消耗。另外,8是Duff策略提出的最优值。
实际测试时发现在IE下可以带来10-20%以上的性能提升,而非IE浏览器中几乎看不到区别。
结束语:在测试过程中发现非IE浏览器下,优化后和优化前的效率差距并不是很大,甚至可以忽略,这说明这些浏览器的JS引擎对
Javascript 相关文章推荐
javascript 模式设计之工厂模式学习心得
Apr 27 Javascript
jquery 插件学习(三)
Aug 06 Javascript
Highcharts 非常实用的Javascript统计图demo示例
Jul 03 Javascript
Javascript实现多彩雪花从天降散落效果的方法
Feb 02 Javascript
js验证真实姓名与身份证号是否匹配
Oct 13 Javascript
js针对ip地址、子网掩码、网关的逻辑性判断
Jan 06 Javascript
jQuery中JSONP的两种实现方式详解
Sep 26 Javascript
JS实现重新加载当前页面或者父页面的几种方法
Nov 30 Javascript
文本溢出插件jquery.dotdotdot.js使用方法详解
Jun 22 jQuery
React Native 搭建开发环境的方法步骤
Oct 30 Javascript
Vue实现回到顶部和底部动画效果
Jul 31 Javascript
Vue2.x和Vue3.x的双向绑定原理详解
Nov 05 Javascript
原生JS可拖动弹窗效果实例代码
Nov 09 #Javascript
当鼠标移动时出现特效的JQuery代码
Nov 08 #Javascript
window.onresize 多次触发的解决方法
Nov 08 #Javascript
javascript阻止scroll事件多次执行的思路及实现
Nov 08 #Javascript
setTimeout和setInterval的深入理解
Nov 08 #Javascript
如何获取select下拉框的值(option没有及有value属性)
Nov 08 #Javascript
jquery ajax修改全局变量示例代码
Nov 08 #Javascript
You might like
PHP一些常用的正则表达式字符的一些转换
2008/07/29 PHP
PHP在线生成二维码(google api)的实现代码详解
2013/06/04 PHP
PHP抓取、分析国内视频网站的视频信息工具类
2014/04/02 PHP
php获取指定日期之间的各个周和月的起止时间
2014/11/24 PHP
smarty模板引擎中自定义函数的方法
2015/01/22 PHP
Yii2 queue的队列使用详解
2019/07/19 PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
2019/09/11 PHP
通过ifame指向的页面高度调整iframe的高度
2006/10/05 Javascript
JavaScript DOM 学习第五章 表单简介
2010/02/19 Javascript
js动态加载以及确定加载完成的代码
2011/07/31 Javascript
Javascript数据结构与算法之列表详解
2015/03/12 Javascript
详解JavaScript语言的基本语法要求
2015/11/20 Javascript
Bootstrap按钮组件详解
2016/04/26 Javascript
全面解析Bootstrap中Carousel轮播的使用方法
2016/06/13 Javascript
bootstrap modal弹出框的垂直居中
2016/12/14 Javascript
AngularJS表单提交实例详解
2017/02/18 Javascript
VSCode 配置React Native开发环境的方法
2017/12/27 Javascript
详解Vue开发微信H5微信分享签名失败问题解决方案
2018/08/09 Javascript
angularjs中判断ng-repeat是否迭代完的实例
2018/09/12 Javascript
微信小程序实现折线图的示例代码
2019/06/07 Javascript
JS中实现一个下载进度条及播放进度条的代码
2019/06/10 Javascript
详解Vue后台管理系统开发日常总结(组件PageHeader)
2019/11/01 Javascript
[01:07:19]DOTA2-DPC中国联赛 正赛 CDEC vs XG BO3 第一场 1月19日
2021/03/11 DOTA
解析Mac OS下部署Pyhton的Django框架项目的过程
2016/05/03 Python
Python中asyncore异步模块的用法及实现httpclient的实例
2016/06/28 Python
Linux-ubuntu16.04 Python3.5配置OpenCV3.2的方法
2018/04/02 Python
python使用mitmproxy抓取浏览器请求的方法
2019/07/02 Python
flask 框架操作MySQL数据库简单示例
2020/02/02 Python
python如何判断IP地址合法性
2020/04/05 Python
如何在VSCode下使用Jupyter的教程详解
2020/07/13 Python
SCHIESSER荷兰官方网站:德国内衣专家
2020/10/09 全球购物
售后服务科岗位职责范文
2013/11/13 职场文书
员工培训心得体会
2013/12/30 职场文书
采购员工作总结范文
2015/08/12 职场文书
《蜜蜂引路》教学反思
2016/02/22 职场文书
python 调用js的四种方式
2021/04/11 Python