第一个循环
在我们开始学习编程的时候就接触过for循环
var arr = new Array(10).fill(1)
for (var i = 0 ; i < arr.length ; i++ ) {
console.log(arr[i])
}
这是很简单的一段,记得当初在用java
或者C++
的时候说过为了提高访问效率需要把数组长度缓存到一个变量中
var arr = new Array(10).fill(1)
var len = arr.length
for (var i = 0 ; i < len ; i++ ) {
console.log(arr[i])
}
那么在我们前端的浏览器、Node里面也是这样吗?
确定for循环写法
因为ES6中新加了let
声明变量的方式,所以有些方式需要增加以下对比;同时使用数组的索引访问增加真实使用场景时间消耗。
var arr = Array.from(new Array(999999)).map((_, i) => i)
// 不缓存数组长度 var
for (var i1 = 0; i1 < arr.length; i1++) {(arr[i1])}
// 缓存数组长度 var
for (var i2 = 0, len2 = arr.length; i2 < len2; i2++) {(arr[i2])}
// 不缓存数组长度 let
for (let i3 = 0; i3 < arr.length; i3++) {(arr[i3])}
// 缓存数组长度 let
for (let i4 = 0, len4 = arr.length; i4 < len4; i4++) {(arr[i4])}
// 数组倒序 var
for (var i5 = arr.length - 1; i5 >= 0; i5--) {(arr[i5])}
// 数组倒序 let
for (let i6 = arr.length - 1; i6 >= 0; i6--) {(arr[i6])}
// 真值判断 var
for (var i6 = 0, item; item = arr[i6]; i6++) {(arr[i6])}
// 真值判断 let
for (let i6 = 0, item; item = arr[i6]; i6++) {(arr[i6])}
// forEach
a.forEach(function(item7) {(item7)
// map
a.map(function(item7) {(item7)
// for of
for(item8 of a) {(item8)}
// while var
var i = 0
while(i < arr.length) {
(arr[i++])
}
// while let
let i = 0
while(i < arr.length) {
(arr[i++])
}
// while let 缓存数组长度
let i = 0, len = arr.length
while(i < len) {
(arr[i++])
}
// for in
for(i8 in a) {(arr[i8])}
浏览器中运行时间
时间判断:使用console.time和console.timeEnd记录每种循环方式的整体执行时间。
内存消耗判断:使用process.memoryUsage()比对执行前后内存中已用到的堆的差值。当然要先判断process
是否存在 typeof process !== 'undefined'
才能使用
局部作用域
# Chrome
局部作用域 不缓存数组长度 var: 3.096923828125 ms
局部作用域 缓存数组长度 var: 2.745849609375 ms
局部作用域 不缓存数组长度 let: 3.07373046875 ms
局部作用域 缓存数组长度 let: 5.197998046875 ms
局部作用域 数组倒序 var: 2.751953125 ms
局部作用域 数组倒序 let: 2.7138671875 ms
局部作用域 真值判断 var: 0.038818359375 ms
局部作用域 真值判断 let: 0.034912109375 ms
局部作用域 forEach: 15.8369140625 ms
局部作用域 map: 30.466064453125 ms
局部作用域 for of: 62.18017578125 ms
局部作用域 while var: 3.120849609375 ms
局部作用域 while let: 3.925048828125 ms
局部作用域 while let 缓存数组长度: 2.635009765625 ms
局部作用域 for in: 367.190185546875 ms
# Firefox
局部作用域 不缓存数组长度 var:285 毫秒
局部作用域 缓存数组长度 var:129 毫秒
局部作用域 不缓存数组长度 let:249 毫秒
局部作用域 缓存数组长度 let:136 毫秒
局部作用域 数组倒序 var:135 毫秒
局部作用域 数组倒序 let:136 毫秒
局部作用域 真值判断 var:1 毫秒
局部作用域 真值判断 let:0 毫秒
局部作用域 forEach:4 毫秒
局部作用域 map:20 毫秒
局部作用域 for of:251 毫秒
局部作用域 while var:235 毫秒
局部作用域 while let:238 毫秒
局部作用域 while let 缓存数组长度:141 毫秒
局部作用域 for in:1556 毫秒
全局作用域
# Chrome
不缓存数组长度 var: 17.60791015625 ms
缓存数组长度 var: 13.378173828125 ms
不缓存数组长度 let: 18.7080078125 ms
缓存数组长度 let: 21.89697265625 ms
数组倒序 var: 26.673095703125 ms
数组倒序 let: 13.211181640625 ms
真值判断 var: 0.013671875 ms
真值判断 let: 0.012939453125 ms
forEach: 25.43505859375 ms
map: 41.006103515625 ms
for of: 38.648193359375 ms
while var: 11.234130859375 ms
while let: 15.410888671875 ms
while let 缓存数组长度: 13.287841796875 ms
for in: 394.052978515625 ms
# Firefox
不缓存数组长度 var:928 毫秒
缓存数组长度 var:948 毫秒
不缓存数组长度 let:428 毫秒
缓存数组长度 let:250 毫秒
数组倒序 var:907 毫秒
数组倒序 let:192 毫秒
真值判断 var:0 毫秒
真值判断 let:1 毫秒
forEach:4 毫秒
map:20 毫秒
for of:362 毫秒
while var:812 毫秒
while let:733 毫秒
while let 缓存数组长度:714 毫秒
for in:1429 毫秒
- 在全局作用域中
- let 整体好于 var
- 局部作用域中
- 用时整体比全局作用域短
- let 和 var无明显区别(性能同全局环境中let差不多);
- Firefox中不缓存数组长度虽然好于在全局环境,但是 let 还是快 var 很多
- Firefox中缓存数组还是有点效果的
总结:全局环境中var需要在全局作用域中查找,let是块作用域中查找,所以性能好;但是在方法的局部作用域中无明显差别。(问题是变量查找不是应该按照作用域链向上查找吗,性能应该差不多吧)
在node中运行时间
局部作用域 不缓存数组长度 var: 3.777ms
局部作用域 缓存数组长度 var: 2.765ms
局部作用域 不缓存数组长度 let: 4.521ms
局部作用域 缓存数组长度 let: 6.082ms
局部作用域 数组倒序 var: 3.672ms
局部作用域 数组倒序 let: 6.448ms
局部作用域 真值判断 var: 0.181ms
局部作用域 真值判断 let: 0.148ms
局部作用域 forEach: 27.809ms
局部作用域 map: 37.32ms
局部作用域 for of: 35.697ms
局部作用域 while var: 3.953ms
局部作用域 while let: 2.643ms
局部作用域 while let 缓存数组长度: 2.58ms
局部作用域 for in: 458.263ms
不缓存数组长度 var: 10.03ms
缓存数组长度 var: 17.274ms
不缓存数组长度 let: 15.581ms
缓存数组长度 let: 17.644ms
数组倒序 var: 14.843ms
数组倒序 let: 14.887ms
真值判断 var: 0.016ms
真值判断 let: 0.02ms
forEach: 45.607ms
map: 54.476ms
for of: 57.174ms
while var: 9.187ms
while let: 13.486ms
while let 缓存数组长度: 15.922ms
for in: 640.726ms
- 是否在全局变量,只对使用for循环的方法有影响(局部变量的时候会快点)
- 是否使用
let
还是var
,对运行时间没有太大影响,使用let
还会慢一点。
总结
对于for循环
的优化
- 是否缓存数组长度没有太大的影响,因为各个编译器对这一块可能做了优化
- 使用
let
还是var
在浏览器(let 全局作用域的时候会快一点)和Node(无影响)表现不一, - 局部作用域中整体比全局作用域好
- 只有一个方法是表现最好的真值判断法,各个端都很理想
因此在数据量相对小的地方可以使用for循环,当较大是可以使用真值判断法,当有复杂需求时可能还要回到for循环。
数组操作性能探究
// TODO
局部环境代码
/* eslint-disable */
var a = Array.from(new Array(999999)).map((_, i) => i)
function loop(key, fun) {
console.time(key)
// if (typeof process !== 'undefined') {
// const startHeap = process.memoryUsage().heapUsed
// fun()
// const endHeap = process.memoryUsage().heapUsed
// const heapDiff = endHeap - startHeap
// console.timeEnd(key)
// console.log('\t已用到的堆的差值: ', heapDiff)
// } else {
fun()
console.timeEnd(key)
// }
}
loop('局部作用域 不缓存数组长度 var', _ => {
for (var i1 = 0; i1 < a.length; i1++) {(a[i1])}
})
loop('局部作用域 缓存数组长度 var', _ => {
for (var i2 = 0, len2 = a.length; i2 < len2; i2++) {(a[i2])}
})
loop('局部作用域 不缓存数组长度 let', _ => {
for (let i3 = 0; i3 < a.length; i3++) {(a[i3])}
})
loop('局部作用域 缓存数组长度 let', _ => {
for (let i4 = 0, len4 = a.length; i4 < len4; i4++) {(a[i4])}
})
loop('局部作用域 数组倒序 var', _ => {
for (var i5 = a.length - 1; i5 >= 0; i5--) {(a[i5])}
})
loop('局部作用域 数组倒序 let', _ => {
for (let i6 = a.length - 1; i6 >= 0; i6--) {(a[i6])}
})
loop('局部作用域 真值判断 var', _ => {
for (var i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
})
loop('局部作用域 真值判断 let', _ => {
for (let i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
})
loop('局部作用域 forEach', _ => {
a.forEach(function(item7) {(item7)})
})
loop('局部作用域 map', _ => {
a.map(function(item7) {(item7)})
})
loop('局部作用域 for of', _ => {
for(item8 of a) {(item8)}
})
loop('局部作用域 while var', _ => {
var i = 0
while(i < a.length) { (a[i++]) }
})
loop('局部作用域 while let', _ => {
let i = 0
while(i < a.length) { (a[i++]) }
})
loop('局部作用域 while let 缓存数组长度', _ => {
let i = 0, len = a.length
while(i < len) { (a[i++]) }
})
// var a1 = Array.from(new Array(99999)).map((_, i) => i)
loop('局部作用域 for in', _ => {
for(i8 in a) {(a[i8])}
})
全局环境代码
/* eslint-disable */
var a = Array.from(new Array(999999)).map((_, i) => i)
console.time('不缓存数组长度 var')
for (var i1 = 0; i1 < a.length; i1++) {(a[i1])}
console.timeEnd('不缓存数组长度 var')
console.time('缓存数组长度 var')
for (var i2 = 0, len2 = a.length; i2 < len2; i2++) {(a[i2])}
console.timeEnd('缓存数组长度 var')
console.time('不缓存数组长度 let')
for (let i3 = 0; i3 < a.length; i3++) {(a[i3])}
console.timeEnd('不缓存数组长度 let')
console.time('缓存数组长度 let')
for (let i4 = 0, len4 = a.length; i4 < len4; i4++) {(a[i4])}
console.timeEnd('缓存数组长度 let')
console.time('数组倒序 var')
for (var i5 = a.length - 1; i5 >= 0; i5--) {(a[i5])}
console.timeEnd('数组倒序 var')
console.time('数组倒序 let')
for (let i6 = a.length - 1; i6 >= 0; i6--) {(a[i6])}
console.timeEnd('数组倒序 let')
console.time('真值判断 var')
for (var i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
console.timeEnd('真值判断 var')
console.time('真值判断 let')
for (let i6 = 0, item; item = a[i6]; i6++) {(a[i6])}
console.timeEnd('真值判断 let')
console.time('forEach')
a.forEach(function(item7) {(item7)})
console.timeEnd('forEach')
console.time('map')
a.map(function(item7) {(item7)})
console.timeEnd('map')
console.time('for of')
for(item8 of a) {(item8)}
console.timeEnd('for of')
console.time('while var')
var i9 = 0
while(i9 < a.length) {(a[i9++])}
console.timeEnd('while var')
console.time('while let')
let i10 = 0
while(i10 < a.length) {(a[i10++])}
console.timeEnd('while let')
console.time('while let 缓存数组长度')
let i11 = 0, len = a.length
while(i11 < len) {(a[i11++])}
console.timeEnd('while let 缓存数组长度')
// var a1 = Array.from(new Array(99999)).map((_, i) => i)
console.time('for in')
for(i8 in a) {(a[i8])}
console.timeEnd('for in')
参考
https://juejin.cn/post/6844903776185221127#heading-5
https://juejin.cn/post/6844903651232710670
https://www.jb51.net/article/80854.htm
网友评论