for,for in , for of 和forEach四者对比

forfor in , for offorEach四者都是用于循环的,但是每个使用场景都是有所不同,接下来进行一个对比

1. 可枚举对象

const person = {
	name: 'henry',
	age: 26,
};

for (const key in person) {
	console.log(key);
}
//输出结果:name age

for (const value of person) {
	console.log(value);
}
// TypeError: person is not iterable

person.forEach((value, index, array) => {
	console.log(value)
})
// TypeError: person.forEach is not a function

这些结果可以看出,只有for…in可以对对象进行遍历(遍历的是对象的key),而for…of和forEach都不能对对象进行遍历,for的编写方式更是没办法遍历

2.可枚举数组

Array.prototype.sayHello = function() {
	console.log("Hello")
}
Array.prototype.str = 'world';
var arr = ['a', 'b', 'c'];
arr.name = '数组';

for (const key in arr) {
	console.log(key);
	console.log(typeof key);
	console.log(arr[key]);
}
// 输出的是:
// 0 string a
// 1 string b
// 2 string c
// name string 数组
// sayHello string [Function]
// str string world

for (const value of arr) {
	console.log(value);
}
// 输出:a,b,c

arr.forEach((value, index) => {
	console.log(index)
	console.log(typeof index);
	console.log(value)
})
// 输出的是:
// 0 number a
// 1 number b
// 2 number c

这些结果看出以下结论:

  • 使用for…in是输出索引值,且索引值是string类型,不仅返回的是数组的下标,而且将数组的原型对象以及数组对象本身属性值都会返回;通过索引值能拿到数组数据;
  • 使用forEach可以输出索引值和数组值,而且不会输出数组的原型对象;
  • 使用for of无法输出索引值,但也不会输出数组的原型对象。
  • 使用for可以输出索引值和数组值,而且不会输出数组的原型对象;

在实际工作开发中,数组的原型对象很可能是不需要的,全部列举出来可能会产生新的问题。
为了解决原型对象这个问题,可以使用 hasOwnProperty

for(let key in arr){
  if(arr.hasOwnProperty(key)) {
    console.log(key);
  }
}
//输出的结果是 0 1 2 name

这个结果能看出来,虽然使用hasOwnProperty,但是数组本身的属性还是会输出

3.迭代字符串

let str = 'hello';

for (let i = 0; i < str.length; i++) {
	console.log(str[i]);
}
//输出结果是 h e l l o

for (let value of str) {
	console.log(value);
}
// //输出结果是 h e l l o

for (let key in str) {
	console.log(str[key]);
}
// 输出的结果是 h e l l o

str.forEach(function(value, index) {
	console.log(value);
});
//输出: TypeError: str.forEach is not a function

结论:for,for…in,for…of可以循环字符串,而forEach不可以

4.迭代arguments类数组对象

(function() {
	for (let argument of arguments) {
		console.log(argument);
	}
	//输出结果是 11 22 33

	for (let i = 0; i < arguments.length; i++) {
		console.log(arguments[i]);
	}
	// //输出结果是 11 22 33

	for (let key in arguments) {
		console.log(arguments[key]);
	}
	//输出结果是 11 22 33

	arguments.forEach(function(value, index) {
		console.log(value);
	});
	//输出:TypeError: arguments.forEach is not a function
})(11, 22, 33);

结果可以得出结论:forEach无法迭代arguments,而for,for…in,for…of都可以迭代arguments

5.迭代map和set

let mapData = new Map([
	['a', 1],
	['b', 2],
	['c', 3]
]);

for (let [key, value] of mapData) {
	console.log(key);
	console.log(value);
}
//输出结果是 a 1 b 2 c 3

for (let item in mapData) {
	console.log(item);
}
//没有输出结果

for (let i = 0; i < mapData.length; i++) {
	console.log(mapData[i]);
}
//没有输出结果

mapData.forEach(function(value, index) {
	console.log(value);
	console.log(index);
});
//输出结果是 a 1 b 2 c 3
let setData = new Set([11, 22, 33])

for (let value of setData) {
	console.log(value);
}
//输出结果是 11 22 33

for (let item in setData) {
	console.log(item);
}
//没有输出结果

for (let i = 0; i < setData.length; i++) {
	console.log(setData[i]);
}
//没有输出结果

setData.forEach(function(value,index) {
	console.log(index);
	console.log(value);
});
//输出结果是 11 11 22 22 33 33

结果可以得出结论:forEach和for…of都可以迭代map/set,而for,for…in无法迭代map/set;

这里有个点要注意,set的forEach中第一个参数是value值,第二个值依然是value值;map的forEach中第一个参数是value值,第二个值是对应的key值

6.是否可中断

var arr = [11, 22, 33];

arr.forEach(function(value, index) {
	console.log(index)
	if (value === 22) {
		return false;
	}
	console.log(value);
});
//输出的结果是 0 11 1 2 33
//使用continue输出: SyntaxError: Illegal continue statement: no surrounding iteration statement
//使用break输出:SyntaxError: Illegal break statement

for (let key in arr) {
	console.log(arr[key]);
	if (arr[key] == 22) {
		continue;
	}
	console.log(key);
}
// 输出的结果是 11 0 22 33 2

for (let value of arr) {
	console.log(value);
	if (value == 22) {
		break;
	}
}
//输出结果是 11 22

这些结果看出以下结论:

  • forEach不能退出循环本身,return只能控制跳出单次循环,不支持break和continue,不可中断循环
  • for…in,for…or以及for都可以中断循环

7.性能效率对比(仅对比连续且数据量大的无漏数组)

var arr = []
console.time('write array')
for (var i = 0; i < 1000 * 1000; i++) {
    arr.push(i)
}
console.timeEnd('write array')

console.time('for loop read')
var sum0 = 0
for (var j = 0; j < 1000 * 1000; j++) {
    sum0 += arr[j]
}
console.timeEnd('for loop read')

console.time('while read')
var sum1 = 0, index = 0
while (index < 1000 * 1000) {
    sum1 += arr[index]
    index++
}
console.timeEnd('while read')

console.time('for in read')
var sum2 = 0
for (let key in arr) {
    sum2 += arr[key]
}
console.timeEnd('for in read')

console.time('for of read')
var sum3 = 0
for (let value in arr) {
    sum3 += value
}
console.timeEnd('for of read')

console.time('for each read')
var sum4 = 0
arr.forEach(function (item) {
    sum4 += item
})
console.timeEnd('for each read')

打印的结果是:

write array: 18.042ms
for loop read: 3.838ms
while read: 3.686ms
for in read: 183.033ms
for of read: 204.108ms
for each read: 16.922ms

多次执行后出入不大,由此可得出结论:
性能效率上:for > for each > for in > for of

部分介绍性能时,考虑的数组数据量和连续性和本次实验的不一致,所有结果也有所差别,过分纠结并没太多意义,只需记得数据量大且无漏时,用for循环遍历数组效率快,且最好把判断用的长度先计算算并缓存起来

总结

  • for…in 适用于纯对象的遍历,能输出可枚举属性,不太适用数组,不能用于map/set对象
  • for…of 适用于无需知道索引值的数组遍历,另外对于字符串,arguments类数组,map/set对象的迭代也更适用
  • forEach适用于需要知道索引值的数组遍历,可遍历map/set对象,但是最大的特点是不能中断
  • for循环适用于数据量大,或者兼容性要求高的情况

for…of其实也可以遍历对象的,但是需要通过Symbol.iterator给要遍历的对象添加这个塑性,这涉及到了另一些东西,有兴趣的可以点击 用symbol.iterator实现用for…of遍历对象,这篇文章

使用 for…in 循环遍历对象的属性时,原型链上的所有属性都将被访问,所以推荐总是使用 hasOwnProperty 方法, 这将会避免原型对象扩展带来的干扰

PS:另有一些不太影响使用场景的特性

async function tmd(value) {
	await new Promise((resolve) => {
		setTimeout(() => {
			console.log(value)
			resolve()
		}, 1000)
	})
}

let arr = [1, 2, 3]

async function folog() {
	for (let value of arr) {
		await tmd(value)
	}
}

async function filog() {
	for (let i in arr) {
		await tmd(arr[i])
	}
}

async function forlog() {
	for (let i = 0; i < arr.length; i++) {
		await tmd(arr[i])
	}
}

async function felog() {
	arr.forEach(async function(value, index, array) {
		await tmd(value)
	})
}

// folog(); //每隔1000毫秒打印一个数字
// filog(); //每隔1000毫秒打印一个数字
// forlog(); //每隔1000毫秒打印一个数字
// felog(); //1000毫秒后全部打印出来

可以得出结论:for,for…in,for…of是继发性的,而forEach是并发性的

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值