for 循环和 while 循环的性能对比
let arr = new Array(9999999).fill(1)
console.time('for-let')
for(let i = 0; i< arr.length; i++){}
console.timeEnd('for-let')
var j = 0
console.time('for-var')
for(j = 0; j< arr.length; j++){}
console.timeEnd('for-var')
console.time('while')
let i = 0
while(i< arr.length){
i ++
}
console.timeEnd('while')
Google中运行结果
思考 不同声明下性能差异产生的原因
- 基于var声明的时候,FOR和WHILE性能差不多「不确定循环次数的情况下使用WHILE」
- 基于let声明的时候,由于
for
中块级作用域的影响,内存得到释放,运行的运行的速度会更快一些。FOR循环性能更好「原理:没有创造全局不释放的变量」
foreach
let arrs = new Array(999999);
console.time('for');
for (let i = 0; i < arrs.length; i++) {
};
console.timeEnd('for');
console.time('forEach');
arrs.forEach((arr) => {
});
console.timeEnd('forEach');
// let arrs = new Array(999999);
let arrs = new Array(999999).fill(1);
console.time('for');
for (let i = 0; i < arrs.length; i++) {
};
console.timeEnd('for');
console.time('forEach');
arrs.forEach((arr) => {
});
console.timeEnd('forEach');
上面两个示例中同样的数据量却有很大的差异这是为什么呢?
fill()
方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
在第二个示例中
arrs = new Array(999999).fill(1);
另外在mdn中对于foreach有这样一段描述
所有我猜测差异是因为第一个示例中的foreach没有处理数组中未初始的值,而第二个示例中foreach因为fill()给数组填充了值,所有才会有这么大的差异,个人猜想如有错误恳请指正
实际使用中需要操作的数组大部分情况都是有值的,可以认为forEach相比for 性能更差
但是对于foreach我们更关注的是他的函数式编程
for循环属于命令式编程(how如何去做 看中的是过程,循环多少轮多少次是可以控制的,还可以改变步长改变循环次数,还可以通过break、continue去结束循环)
forEach属于函数式编程,用起来比for方便,性能比for更差些(what更关注结果,把执行过程封装起来,内部实现封装,自己控制不了循环次数,无法管控过程 不可以中断结束循环。所以会把循环都走一遍)
而在数据量小的时候性能差异不大
let arrs = new Array(9999).fill(1);
console.time('for');
for (let i = 0; i < arrs.length; i++) {
};
console.timeEnd('for');
console.time('forEach');
arrs.forEach((arr) => {
});
console.timeEnd('forEach');
</script>
思考 什么情况适合使用foreach进行循环遍历呢
forEach属于函数式编程,数据量小的时候 使用forEach循环遍历操作数据更方便
数据量比较大量级时为提升性能,我倾向于使用for循环自己在for循环中操作数据
思考:在 forEach 中怎么中断循环
return 无法跳出循环
foreach使用return 只是中止本次继续执行,而不是终止循环,有点像for循环里面的continue,然后forEach 里面的return是没有返回值的效果的
可以自己throw new Error("end");再try catch捕获异常
除了抛出异常以外,没有办法中止或跳出
forEach()
循环。如果你需要中止或跳出循环,forEach()
方法不是应当使用的工具。
实现 forEach
看v8引擎中js源码 使用的是for循环实现
v8/array.js at eff24bef5c0f071008bdd4bcee3a86384e90c90b · v8/v8 · GitHub
Array.prototype.myForEach = function (callback, context) {
let i = 0,
than = this,
len = this.length;
context = context ? window : context;//forEach每循环一次把传进去的回调函数执行一次 还有
//第二个参数是每一回调执行内部this的指向 不传的情况下默认window
for (; i < len; i++) {
typeof callback === 'function' ? callback.call(context, than[i], i, than) : null
}
}
for in 循环
for in
的循环性能循环很差。性能差的原因是因为:迭代当前对象中所有可枚举的属性(私有属性大部分是可枚举的,公有属性【出现在所属类的原型上的】,也有部分是可枚举的),查找机制一定会搞到原型链上去
let obj = new Array(9999).fill(1);
console.time('for-in');
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break;// 阻止获取原型链上的公有属性 fn 减少循环提示性能
// console.log(key);
}
console.timeEnd('for-in');
缺点
1、遍历顺序以数字优先,由小到大遍历
2、无法遍历symbol属性
3、可以遍历到公有中的可枚举属性 可以使用 hasOwnProperty
来阻止遍历公有属性。
思考 怎么获取 Symbol 属性
(Object.keys()可以拿到所有非symbol属性 Object.getOwnPropertySymbols(拿到所有symbol属性。拼接拿到所有)
let obj = {
name: 'zhufeng',
age: 12,
[Symbol('AA')]: 100,
0: 200,
1: 300
};
let keys = Object.keys(obj);
if (typeof Symbol !== "undefined") keys = keys.concat(Object.getOwnPropertySymbols(obj));
keys.forEach(key => {
console.log('属性名:', key);
console.log('属性值:', obj[key]);
{
)
for of 循环
let arr = new Array(999999).fill(1)
console.time('forOf')
for(const value of arr){}
console.timeEnd('forOf')
for of 循环的原理是
按照是否有迭代器规范来循环的
,所有带有Symbol.iterator
的都是实现了迭代器规范,比如数组一部分类数组,Set,Map...
,对象没有实现 Symbol.iterator 规范
,所以不能使用for of
循环。
- 使用
for of
循环,首先会先执行Symbol.iterator
属性对应的函数且返回一个对象 - 对象内包含一个函数
next()
循环一次执行一次next()
,next()
中又返回一个对象 - 这个对象内包含两个值分别是
done:代表循环是否结束,true 代表结束;value:代表每次返回的值
let arr = [1,2,3,4]
arr[Symbol.iterator] = function () {
let self = this,
index = 0;
return {
next() {
if(index > self.length-1){
return {
done: true,
value: undefined
}
}
return {
done: false,
value: self[index++]
}
}
}
}
console.time('forOf');
for (const val of arr) {
console.log(val);
}
console.timeEnd('forOf');
思考,如何让普通的类数组可以使用 for of 循环
给类数组对象添加Symbol.iterator
接口规范就可以了。
类数组被需具备和数组类试的结果属性名从
0, 1, 2...
开始,且必须具备length
属性
// 类数组对象「默认不具备迭代器规范」
let obj = {
0: 200,
1: 300,
2: 400,
length: 3
};
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let val of obj) {
console.log(val);
}