循环的操作在代码中是必不可少的,本文将对Js中的for,while,forEach,for in,for of几种常用的循环进行对比及性能分析。
注意:本次对比的环境都是在浏览器端,并未测试node端的数据表现
for循环是自己控制循环过程,控制循环多少次。
我们来看看性能如何
let arr=new Array(9999999).fill(0);//其他循环的arr测试用例也是这个
console.time('for');
for(let i=0;i<arr.length;i++){}
console.timeEnd('for');
打开控制台,输出的是for: 6.1259765625 ms
(经过多次测试,时间都是在6s多)
while不知道循环次数,由条件语句控制。
console.time('while');
let i=0;
while(i<arr.length){i++;}
console.timeEnd('while');
打开控制台,输出的是while: 20.0771484375 ms
对比for循环发现,while循环的性能差了很多。
为什么会有这样的差异呢?
我们再把变量 i 的声明方式改成var来看看
let arr=new Array(9999999).fill(0);
console.time('for');
for(var i=0;i<arr.length;i++){}
console.timeEnd('for');
console.time('while');
var i=0;
while(i<arr.length){i++;}
console.timeEnd('while');
原因:基于let声明的时候,for循环没有创造全局不释放的变量,for循环的时候会释放上一次循环的let 变量。var声明的变量都是全局变量
for(let i=0;i<arr.length;i++)
//创建的i是每次循环创建的块级作用域下面的,没有创造全局的,随着每轮循环结束释放
let i=0;
while(i<arr.length)//i在全局上下文中不被释放
我们可以得出结论:for跟while本身区别不大,但是
基于var声明的时候,for和while性能差不多
基于let声明的时候,for循环性能更好
一般情况下,如果我们明确知道循环多少次的话,用for循环,循环的次数需要条件判断的话就用while。
forEach是数组提供的遍历数组的方法
console.time('forEach');
arr.forEach(function (item){})
console.timeEnd('forEach');
可以看出,forEach明显比for和while都更慢。
因为是封装好的,内部也要处理其他逻辑,没有for和while那么清爽。用起来方便,好维护,但是我们无法管控过程,没办法中途终止循环,在性能上也有所消耗。
手写版forEach:
Array.prototype.forEach = function forEach(callback, context) {
//谁调用forEach,这里的this就是谁,所以这里是arr,用self保存起来,self[i]就表示每次循环的当前项
let self = this,
i = 0,
len = self.length;
//这里做个判断,forEach的第二个参数表示改变forEach的this值,也就是调用了call,如果没传第二个参数,默认是window
context = context == null ? window : context;
for (; i < len; i++) {
typeof callback === "function" ? callback.call(context, self[i], i) : null;
}
}
let arr = [1, 2, 3, 4]
arr.forEach(function (item) {
console.log(item);
})
注意:forEach方法在数组元素为空时会跳过执行回调函数, 也就是forEach会忽略掉 [empty,…]
console.time('for in');
for(let key in arr){}
console.timeEnd('for in');
可以看出for in循环的性能是非常差的,这和for in本身的循环机制有关。
for in循环会迭代当前对象中所有可枚举的属性,其中私有属性大部分是可枚举的,查找机制上一定会搞到原型链上去,按原型链一级级查找很耗费性能。
另外,for in循环还有些小问题:
①遍历顺序以数字优先
②无法遍历Symbol属性
③可以遍历到公有中可枚举的
Object.prototype.fn=function fn(){};
let obj={
name:'zhangSan',
age:12,
[Symbol('AA')]:100,
0:100,
1:200
}
for(let key in obj){
console.log(key);
}
console.time('for of');
for(let val of arr){}
console.timeEnd('for of');
for of循环的原理是按照迭代器规范进行遍历。
手动实现迭代器:
let arr = [1, 2, 3, 4];
arr[Symbol.iterator] = function () {
let self = this,
index = 0;
return {
//必须具备next方法,执行一次next方法,拿到结构中的某一项的值
//done:false value:每一次获取的值
next() {
if (index > self.length - 1) {
return {
done: ture,
value: undefined
};
}
return {
done: false,
value: self[index++]
};
}
};
};
console.time('for of');
for (const val of arr) {
console.log(val);
}
console.timeEnd('for of');
应用:对象默认不具备迭代器规范,如何让类数组对象具备呢?
我们来看看不具备迭代器的时候:
let obj={
0:100,
1:200,
2:300,
length:3
}
for(let val of obj){
console.log(val);
}
let obj={
0:100,
1:200,
2:300,
length:3
}
obj[Symbol.iterator]=Array.prototype[Symbol.iterator];
for(let val of obj){
console.log(val);
}
以上就是Js几种循环方式的比较,感谢您的阅读,希望这篇文章能对你有所帮助,麻烦点赞收藏一下吧