深入探究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 相关文章推荐
javascript eval函数深入认识
Feb 21 Javascript
6个DIV 135或246间隔一秒轮番显示效果
Jul 24 Javascript
js函数的延迟加载实现代码
Oct 11 Javascript
尝试在让script的type属性等于text/html
Jan 15 Javascript
Extjs4中的分页应用结合前后台
Dec 13 Javascript
js检测浏览器版本、核心、是否移动端示例
Apr 24 Javascript
vue轮播图插件vue-awesome-swiper
Nov 27 Javascript
详解Vue 多级组件透传新方法provide/inject
May 09 Javascript
node.js的Express服务器基本使用教程
Jan 09 Javascript
使用webpack搭建vue项目实现脚手架功能
Mar 15 Javascript
JS实现纸牌发牌动画
Jan 19 Javascript
vue组件vue-esign实现电子签名
Apr 21 Vue.js
简单谈谈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面向对象学习笔记之二 生成对象的设计模式
2012/10/06 PHP
php实现读取手机客户端浏览器的类
2015/01/09 PHP
javascript基本语法分析说明
2008/06/15 Javascript
jquery自动完成插件(autocomplete)应用之PHP版
2009/12/15 Javascript
js实现飞入星星特效代码
2014/10/17 Javascript
JQuery简单实现锚点链接的平滑滚动
2015/05/03 Javascript
jQuery EasyUI之DataGrid使用实例详解
2016/01/04 Javascript
js只执行1次的函数示例
2016/07/20 Javascript
jQuery实现滚动条滚动到子元素位置(方便定位)
2017/01/08 Javascript
Angular 2 利用Router事件和Title实现动态页面标题的方法
2017/08/23 Javascript
浅析Angular19 自定义表单控件
2018/01/31 Javascript
Vue-cropper 图片裁剪的基本原理及思路讲解
2018/04/17 Javascript
Node.js模拟发起http请求从异步转同步的5种用法
2018/09/26 Javascript
js实现中文实时时钟
2020/01/15 Javascript
原生js实现html手机端城市列表索引选择城市
2020/06/24 Javascript
python对数组进行反转的方法
2015/05/20 Python
使用python在本地电脑上快速处理数据
2017/06/22 Python
利用python求解物理学中的双弹簧质能系统详解
2017/09/29 Python
使用Python的Django和layim实现即时通讯的方法
2018/05/25 Python
python异常触发及自定义异常类解析
2019/08/06 Python
PyQt+socket实现远程操作服务器的方法示例
2019/08/22 Python
pytorch下使用LSTM神经网络写诗实例
2020/01/14 Python
使用Python和百度语音识别生成视频字幕的实现
2020/04/09 Python
python可迭代对象去重实例
2020/05/15 Python
ubuntu16.04升级Python3.5到Python3.7的方法步骤
2020/08/20 Python
HTML5中的Web Notification桌面通知功能的实现方法
2019/07/29 HTML / CSS
高中毕业自我鉴定范文
2013/10/02 职场文书
秋季校运动会广播稿
2014/02/23 职场文书
精彩广告词大全
2014/03/19 职场文书
战略合作协议书范本
2014/04/18 职场文书
旅游局领导班子“四风”问题对照检查材料思想汇报
2014/09/29 职场文书
晚会开场白和结束语
2015/05/29 职场文书
运动会三级跳加油稿
2015/07/21 职场文书
oracle DGMGRL ORA-16603报错的解决方法(DG Broker)
2021/04/06 Oracle
聊一聊python常用的编程模块
2021/05/14 Python
OpenCV-Python实现图像平滑处理操作
2021/06/08 Python