深入探究JavaScript中for循环的效率问题及相关优化


Posted in Javascript onMarch 13, 2016

Underscore.js库

你一天(一周)内写了多少个循环了?

var i;
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 doSomeWorkOn(someThing);
}

这当然无害,但这种写法非常丑而且奇怪,这也不是真正需要抱怨的。但这种写法太平庸了。

var i,
 j;
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 for(j = 0; j < someThing.stuff.length; j++) {
   doSomeWorkOn(someThing.stuff[j]);
 }
}

你在扩展糟糕的代码,在你抛出一大堆if前,你已经精神错乱了。
我在两年里没有写一个循环(loop)。
“你在说什么?”
这是真的,一个冷笑话。其实不是一个都没有(好吧,我确实写了几个),因为我不写循环(loops),我的代码更容易理解。
怎么做的呢?

_.each(someArray, function(someThing) {
 doSomeWorkOn(someThing);
})

或者更好一点:

_.each(someArray, doSomeWorkOn);

这就是underscorejs所做到的。干净,简单,易读,短,没有中间变量,没有成堆的分号,简单非常优雅。
这是另外一些例子。

var i,
 result = [];
for(i = 0; i < someArray.length; i++) {
 var someThing = someArray[i];
 // 打到这,我已经手疼了
 if(someThing.isAwesome === true) {
   result.push(someArray[i]);
 }
}

同样,一个使用循环浪费时间的典型用例。即便这些网站是宣传禁烟和素食主义的,看到这些代码我也感到义愤。看看简单的写法。

var result = _.filter(someArray, function(someThing) {
 return someThing.isAwesome === true;
})

像underscore中的filter(过滤)的名字那样,随手写的3行代码就可以给你一个新的数组(array)。
或者你想把这些数组转换成另外一种形式?

var result = _.map(someArray, function(someThing) {
 return trasformTheThing(someThing);
})

上面三个例子在日常生活中已经够用了,但这些功能还不足矣让underscore放到台面上。

var grandTotal = 0,
 somePercentage = 1.07,
 severalNumbers = [33, 54, 42],
 i; // don't forget to hoist those indices;
for(i = 0; i < severalNumbers.length; i++) {
 var aNumber = severalNumbers[i];
 grandTotal += aNumber * somePercentage;
}

underscore版本

var somePercentage = 1.07,
 severalNumbers = [33, 54, 42],
 grandTotal;
grandTotal = _.reduce(severalNumbers, function(runningTotal, aNumber) {
 return runningTotal + (aNumber * somePercentage);
}, 0)

这个刚开始看上去可能有点怪,我查了下关于reduce的文档,知道了它的存在。因为我拒绝使用循环,所以它是我的首选。上面这些东西仅仅是入门,underscorejs库还有一大堆牛B的功能。

30天不使用循环的挑战。

在一下一个30天里,不要使用任何循环,如果你看到一堆讨厌和粗糙的东西,用each或者map将他们替换掉。再用一点reducing。

你需要注意到,Underscore是通往函数式编程的。一种看得见,看不见的方式。一条很好的途径。

OurJS注*目前现代浏览器已经支持each, filter, map, reduce方法,但underscore库可以实现对旧版IE的兼容,下面是使用ES5原生方法写的例子:

[3,4,5,3,3].forEach(function(obj){
  console.log(obj);
});

[1,2,3,4,5].filter(function(obj){
  return obj < 3
});

[9,8,5,2,3,4,5].map(function(obj){
  return obj + 2;
});

[1,2,3,4,5].reduce(function(pre, cur, idx, arr) {
  console.log(idx);  //4 个循环: 2-5
  return pre + cur;
});  //15

//sort方法同样很有用
[9,8,5,2,3,4,5].sort(function(obj1, obj2){
  return obj1 - obj2;
});

for in与for loop

有人提出for in的效率要比for loop(循环)的效率低非常多。现在我们测试一下在不同浏览器中使用for in, for loop和forEach在处理大数组时的效率究竟如何。

目前绝大部分开源软件都会在for loop中缓存数组长度,因为普通观点认为某些浏览器Array.length每次都会重新计算数组长度,因此通常用临时变量来事先存储数组长度,如:

for (var idx = 0, len = testArray.length; idx < len; idx++) {
 //do sth.
}

我们也会测试一下缓存与不缓存时的性能差异。

同时在每个测试循环中添加求和运算,来表明其不是空循环。

for (var idx = 0, len = testArray.length; idx < len; idx++) {
 //do sth.
}

我们也会测试一下缓存与不缓存时的性能差异。

同时在每个测试循环中添加求和运算,来表明其不是空循环。

测试代码如下,点击运行即可查看
HTML 代码

<h4 id="browser"></h4>
<table id="results" class="table"></table>

JavaScript 代码

function () {

 //准备测试数据, 有200万条数据的大数组
 var testArray = []
  , testObject = {}
  , idx
  , len = 2000000
  , tmp = 0
  , $results = $("#results")
  , $browser = $("#browser")
  ;

 $browser.html(navigator.userAgent);
 $results.html('');

 for (var i = 0; i < len; i++) {
  var number = Math.random(); //若希望加快运算速度可使用取整:Math.random() * 10 | 0
  testArray.push(number);
  testObject[i] = number;
 }

 $results.append('<tr><th>测试代码</th><th>计算结果</th><th>所需时间,毫秒</th></tr>');

 //测试函数
 var test = function(testFunc) {
  var startTime
   , endTime
   , result
   ;

  startTime = new Date();
  tmp = 0;
  testFunc();
  endTime  = new Date();

  //计算测试用例(Test Case)运行所需要的时间
  result = endTime - startTime;
  $results.append('<tr><td><pre>{0}</pre></td><td>{1}</td><td>{2}</td></tr>'.format(testFunc.toString(), tmp | 0, result));
 };


 test(function() {
  //测试for in 的效率
  for (idx in testArray) {
   tmp += testArray[idx]; //经测试,idx是string类型,可能是慢的原因之一
  }
 });

 test(function() {
  //测试for loop循环的效率
  for (idx = 0, len = testArray.length; idx < len; idx++) {
   tmp += testArray[idx];
  }
 });

 test(function() {
  //测试forEach的效率
  testArray.forEach(function(data) {
   tmp += data;
  });
 });

 test(function() {
  //测试不缓存Array.length时效率
  for (idx = 0; idx < testArray.length; idx++) {
   tmp += testArray[idx];
  }
 });

 test(function() {
  //测试使用{} (Object) 存健值对时,使用for in的效率如何
  for (idx in testObject) {
   tmp += testObject[idx];
  }
 });
 
 test(function() {
  //测试从{} Object查值时的效率如何(这里的健key值事先己知)
  for (idx = 0, len = testArray.length; idx < len; idx++) {
   tmp += testObject[idx];
  }
 });

}

运行 [需稍等片刻]
测试结果
测试结果可能因计算而异,这是在我机器上运行用,Firefox, Chrome, IE三者测试结果拼接的一张汇总。

深入探究JavaScript中for循环的效率问题及相关优化

以下是几个观察到的结论

  • for in比for loop慢非常多,在Chrome中至少慢20倍
  • FF对forEach(ES5)做了优化,性能比for loop还要好一点,但Chrome/IEn性能均较差
  • FF/Chrome缓存Array.length均比直接用时要慢一点。除IE最新版缓存后性能提升微乎其微(这一点非常意外)
  • 在某些情况下,FF的JS引擎性能似乎比V8要好些
Javascript 相关文章推荐
jquery遍历input取得input的name
Apr 27 Javascript
写出更好的JavaScript程序之undefined篇(中)
Nov 23 Javascript
早该知道的7个JavaScript技巧
Mar 27 Javascript
javascript验证上传文件的类型限制必须为某些格式
Nov 14 Javascript
原生js验证简洁注册登录页面
Dec 17 Javascript
详解AngularJS controller调用factory
May 19 Javascript
Angular中响应式表单的三种更新值方法详析
Aug 22 Javascript
React中使用UEditor百度富文本的方法
Aug 22 Javascript
react的滑动图片验证码组件的示例代码
Feb 27 Javascript
React Hooks 实现和由来以及解决的问题详解
Jan 17 Javascript
JavaScript DOM常用操作代码汇总
Jul 03 Javascript
js实现简单图片拖拽效果
Feb 22 Javascript
简单谈谈json跨域
Mar 13 #Javascript
详解JavaScript中数组和字符串的lastIndexOf()方法使用
Mar 13 #Javascript
Node.js编写爬虫的基本思路及抓取百度图片的实例分享
Mar 12 #Javascript
JavaScript中循环遍历Array与Map的方法小结
Mar 12 #Javascript
Node.js的Express框架使用上手指南
Mar 12 #Javascript
Node.js项目中调用JavaScript的EJS模板库的方法
Mar 11 #Javascript
JavaScript操作HTML DOM节点的基础教程
Mar 11 #Javascript
You might like
PHP 5.0对象模型深度探索之对象复制
2008/03/27 PHP
thinkPHP实现MemCache分布式缓存功能
2016/03/23 PHP
PHP自定义函数实现assign()数组分配到模板及extract()变量分配到模板功能示例
2018/05/23 PHP
Vagrant(WSL)+PHPStorm+Xdebu 断点调试环境搭建
2019/12/13 PHP
JS 文字符串转换unicode编码函数
2009/05/30 Javascript
jquery $(document).ready() 与window.onload的区别
2009/12/28 Javascript
JS 屏蔽按键效果与改变按键效果的示例代码
2013/12/24 Javascript
javascript遍历控件实例详细解析
2014/01/10 Javascript
jquery五角星评分插件示例分享
2014/02/21 Javascript
浅谈JavaScript实现面向对象中的类
2014/12/09 Javascript
jQuery中slideUp()方法用法分析
2014/12/24 Javascript
JavaScript中的console.log()函数详细介绍
2014/12/29 Javascript
分享15个大家都熟知的jquery小技巧
2015/12/02 Javascript
jQuery validate插件功能与用法详解
2016/12/15 Javascript
angular ng-model 无法获取值的处理方法
2018/10/02 Javascript
vue.js路由mode配置之去掉url上默认的#方法
2019/11/01 Javascript
jquery.validate自定义验证用法实例分析【成功提示与择要提示】
2020/06/06 jQuery
python实现域名系统(DNS)正向查询的方法
2016/04/19 Python
python对绑定事件的鼠标、按键的判断实例
2019/07/17 Python
python基于json文件实现的gearman任务自动重启代码实例
2019/08/13 Python
python集合的创建、添加及删除操作示例
2019/10/08 Python
Python3操作MongoDB增册改查等方法详解
2020/02/10 Python
Mysql数据库反向生成Django里面的models指令方式
2020/05/18 Python
浅谈three.js中的needsUpdate的应用
2012/11/12 HTML / CSS
YesStyle美国/全球:购买亚洲时装、美容化妆品和生活百货
2017/01/16 全球购物
End Clothing美国站:英国男士潮牌商城
2018/04/20 全球购物
Probikekit欧盟:在线公路自行车专家
2019/07/12 全球购物
初级Java程序员面试题
2016/03/03 面试题
公务员年总结的自我评价
2013/10/25 职场文书
模范家庭事迹材料
2014/02/10 职场文书
促销活动总结怎么写
2014/06/25 职场文书
搭讪开场白台词大全
2015/05/28 职场文书
胡桃夹子观后感
2015/06/11 职场文书
生日宴会家属答谢词
2015/09/29 职场文书
礼仪培训心得体会
2016/01/22 职场文书
30岁前绝不能错过的10本书
2019/08/08 职场文书