JS数组循环的性能和效率分析(for、while、forEach、map、for of)

您所在的位置:网站首页 map遍历的几种方式和效率问题 JS数组循环的性能和效率分析(for、while、forEach、map、for of)

JS数组循环的性能和效率分析(for、while、forEach、map、for of)

2024-07-15 14:48:45| 来源: 网络整理| 查看: 265

前言

前端开发中经常涉及到数组的相关操作:去重、过滤、求和、数据二次处理等等。都需要我们对数组进行循环。为了满足各种需求,JS除了提供最简单的for循环,在ES6和后续版本中也新增的诸如:map、filter、some、reduce等实用的方法。因为各个方法作用不同,简单的对所有涉及到循环的方法进行执行速度比较,是不公平的,也是毫无意义的。那么我们就针对最单纯的以取值为目的的循环进行一次性能和效率测试,用肉眼可见的方式,对JS中常见的这些数组循环方式进行一次探讨。

从最简单的for循环说起 for循环常见的四种写法,不啰嗦,直接上代码 const persons = ['郑昊川', '钟忠', '高晓波', '韦贵铁', '杨俊', '宋灿'] // 方法一 for (let i = 0; i < persons.length; i++) { console.log(persons[i]) } // 方法二 for (let i = 0, len = persons.length; i < len; i++) { console.log(persons[i]) } // 方法三 for (let i = 0, person; person = persons[i]; i++) { console.log(person) } // 方法四 for (let i = persons.length; i--;) { console.log(persons[i]) }

第一种方法是最常见的方式,不解释。

第二种方法是将persons.length缓存到变量len中,这样每次循环时就不会再读取数组的长度。

第三种方式是将取值与判断合并,通过不停的枚举每一项来循环,直到枚举到空值则循环结束。执行顺序是:

第一步:先声明索引i = 0和变量person第二步:取出数组的第i项persons[i]赋值给变量person并判断是否为Truthy第三步:执行循环体,打印person第四步:i++。

当第二步中person的值不再是Truthy时,循环结束。方法三甚至可以这样写 js for (let i = 0, person; person = persons[i++];) { console.log(person) }

第四种方法是倒序循环。执行的顺序是: 第一步:获取数组长度,赋值给变量i第二步:判断i是否大于0并执行i--第三步:执行循环体,打印persons[i],此时的i已经-1了 从后向前,直到 i === 0为止。这种方式不仅去除了每次循环中读取数组长度的操作,而且只创建了一个变量 i。 四种for循环方式在数组浅拷贝中的性能和速度测试 先造一个足够长的数组作为要拷贝的目标(如果`i`值过大,到亿级左右,可能会抛出JS堆栈跟踪的报错) const ARR_SIZE = 6666666 const hugeArr = new Array(ARR_SIZE).fill(1) 然后分别用四种循环方式,把数组中的每一项取出,并添加到一个空数组中,也就是一次数组的浅拷贝。并通过[console.time](https://developer.mozilla.org/en-US/docs/Web/API/Console/time)和[console.timeEnd](https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd)记录每种循环方式的整体执行时间。通过[process.memoryUsage()](http://www.ruanyifeng.com/blog/2017/04/memory-leak.html)比对执行前后内存中已用到的堆的差值。 /* node环境下记录方法执行前后内存中已用到的堆的差值 */ function heapRecord(fun) { if (process) { const startHeap = process.memoryUsage().heapUsed fun() const endHeap = process.memoryUsage().heapUsed const heapDiff = endHeap - startHeap console.log('已用到的堆的差值: ', heapDiff) } else { fun() } } // 方法一,普通for循环 function method1() { var arrCopy = [] console.time('method1') for (let i = 0; i < hugeArr.length; i++) { arrCopy.push(hugeArr[i]) } console.timeEnd('method1') } // 方法二,缓存长度 function method2() { var arrCopy = [] console.time('method2') for (let i = 0, len = hugeArr.length; i < len; i++) { arrCopy.push(hugeArr[i]) } console.timeEnd('method2') } // 方法三,取值和判断合并 function method3() { var arrCopy = [] console.time('method3') for (let i = 0, item; item = hugeArr[i]; i++) { arrCopy.push(item) } console.timeEnd('method3') } // 方法四,i--与判断合并,倒序迭代 function method4() { var arrCopy = [] console.time('method4') for (let i = hugeArr.length; i--;) { arrCopy.push(hugeArr[i]) } console.timeEnd('method4') } 分别调用上述方法,每个方法重复执行12次,去除一个最大值和一个最小值,求平均值(四舍五入),最终每个方法执行时间的结果如下表(测试机器:`MacBook Pro (15-inch, 2017) 处理器:2.8 GHz Intel Core i7 内存:16 GB 2133 MHz LPDDR3` 执行环境:`node v10.8.0`): -方法一方法二方法三方法四第一次152.201ms156.990ms152.668ms152.684ms第二次150.047ms159.166ms159.333ms152.455ms第三次155.390ms151.823ms159.365ms149.809ms第四次153.195ms155.994ms155.325ms150.562ms第五次151.823ms154.689ms156.483ms148.067ms第六次152.715ms154.677ms153.135ms150.787ms第七次152.084ms152.587ms157.458ms152.572ms第八次152.509ms153.781ms153.277ms152.263ms第九次154.363ms156.497ms151.002ms154.310ms第十次153.784ms155.612ms161.767ms153.487ms平均耗时152.811ms155.182ms155.981ms151.700ms用栈差值238511136Byte238511352Byte238512048Byte238511312Byte 意不意外?惊不惊喜?想象之中至少方法二肯定比方法一更快的!但事实并非如此,不相信眼前事实的我又测试了很多次,包括改变被拷贝的数组的长度,长度从百级到千万级。最后发现:在`node`下执行完成同一个数组的浅拷贝任务,耗时方面四种方法的差距微乎其微,有时候排序甚至略有波动。 内存占用方面:**方法一 小结 考虑到在不同环境或浏览器下的性能和效率: `推荐`:**第四种**`i–`倒序循环的方式。在奇舞团的这篇文章——[嗨,送你一张Web性能优化地图](https://juejin.im/entry/5b4da7efe51d45198f5c6234)的`2.3 流程控制`小节里也略有提及这种方式。 `不推荐`:**第三种**方式。主要是因为当数组里存在非`Truthy`的值时,比如`0`和`”`,会导致循环直接结束。 while循环以及ES6+的新语法forEach、map和for of,会更快吗? 不啰嗦,实践是检验真理的唯一标准 // 方法五,while function method5() { var arrCopy = [] console.time('method5') let i = 0 while (i < hugeArr.length) { arrCopy.push(hugeArr[i++]) } console.timeEnd('method5') } // 方法六,forEach function method6() { var arrCopy = [] console.time('method6') hugeArr.forEach((item) => { arrCopy.push(item) }) console.timeEnd('method6') } // 方法七,map function method7() { var arrCopy = [] console.time('method7') arrCopy = hugeArr.map(item => item) console.timeEnd('method7') } // 方法八,for of function method8() { var arrCopy = [] console.time('method8') for (let item of hugeArr) { arrCopy.push(item) } console.timeEnd('method8') } 测试方法同上,测试结果: -方法五方法六方法七方法八第一次151.380ms221.332ms875.402ms240.411ms第二次152.031ms223.436ms877.112ms237.208ms第三次150.442ms221.853ms876.829ms253.744ms第四次151.319ms222.672ms875.270ms243.165ms第五次150.142ms222.953ms877.940ms237.825ms第六次155.226ms225.441ms879.223ms240.648ms第七次151.254ms219.965ms883.324ms238.197ms第八次151.632ms218.274ms878.331ms240.940ms第九次151.412ms223.189ms873.318ms256.644ms第十次155.563ms220.595ms881.203ms234.534ms平均耗时152.040ms221.971ms877.795ms242.332ms用栈差值238511400Byte238511352Byte53887824Byte191345296Byte 在`node`下,由上面的数据可以很明显的看出,`forEach`、`map`和`for of` 这些`ES6+`的语法并没有传统的`for`循环或者`while`循环快,特别是`map`方法。但是由于`map`有返回值,无需额外调用新数组的`push`方法,所以在执行浅拷贝任务上,内存占用很低。而`for of`语法在内存占用上也有一定的优势。顺便提一下:`for循环 while循环 for of 循环`是可以通过`break`关键字跳出的,而`forEach map`这种循环是无法跳出的。 但是随着执行环境和浏览器的不同,这些语法在执行速度上也会出现偏差甚至反转的情况,直接看图: `谷歌浏览器` 谷歌 `火狐浏览器` 火狐 `safari浏览器下` safari 可以看出: 谷歌浏览器中ES6+的循环语法会普遍比传统的循环语法慢,但是火狐和safari中情况却几乎相反。谷歌浏览器的各种循环语法的执行耗时上差距并不大。但map特殊,速度明显比其他几种语法慢,而在火狐和safari中却出现了反转,map反而比较快!苹果大法好 总结

之前有听到过诸如“缓存数组长度可以提高循环效率”或者“ES6的循环语法更高效”的说法。说者无心,听者有意,事实究竟如何,实践出真知。抛开业务场景和使用便利性,单纯谈性能和效率是没有意义的。 ES6新增的诸多数组的方法确实极大的方便了前端开发,使得以往复杂或者冗长的代码,可以变得易读而且精炼,而好的for循环写法,在大数据量的情况下,确实也有着更好的兼容和多环境运行表现。当然本文的讨论也只是基于观察的一种总结,并没有深入底层。而随着浏览器的更新,这些方法的孰优孰劣也可能成为玄学。目前发现在Chrome Canary 70.0.3513.0下for of 会明显比Chrome 68.0.3440.84快。如果你有更深入的见解或者文章,也不妨在评论区分享,小弟的这篇文章也权当抛砖引玉。如果你对数组的其他循环方法的性能和效率也感兴趣,不妨自己动手试一试,也欢迎评论交流。

本文的测试环境:node v10.8.0、Chrome 68.0.3440.84、Safari 11.1.2 (13605.3.8)、Firefox 60.0



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭