参考:CRMEB的推文;JavaScript for 循环 | 菜鸟教程
目录
本质区别
for循环是一种从js推出时就提出来的遍历行为;
forEach则是在后来ES5中提出的迭代行为
遍历:指的对数据结构的每一个成员进行有规律的且为一次访问的行为。
迭代:迭代是递归的一种特殊形式,是迭代器提供的一种方法,默认情况下是按照一定顺序逐个访问数据结构成员。迭代也是一种遍历行为。
let arr = [1, 2, 3, 4] // 可迭代对象
let iterator = arr[Symbol.iterator]() // 调用 Symbol.iterator 后生成了迭代器对象
console.log(iterator.next()); // {value: 1, done: false} 访问迭代器对象的next方法
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
可以看出,只要是可迭代对象,调用内部的 Symbol.iterator
都会提供一个迭代器,并根据迭代器返回的next
方法来访问内部
语法区别
对forEach的传参
arr.forEach((self,index,arr) =>{},this)
- self: 数组当前遍历的元素,默认从左往右依次获取数组元素。
- index: 数组当前元素的索引,第一个元素索引为0,依次类推。
- arr: 当前遍历的数组。
- this: 回调函数中this指向。
如果我们需要对一个数组去重,for循环体系长这样
let arr1 = [1, 2, 1, 3, 1];
let arr2 = [];
for (let i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) == -1) {
arr2.push(arr1[i]);
}
}
console.log(arr2);
而forEach长这样
let arr1 = [1, 2, 1, 3, 1];
let arr2 = [];
arr1.forEach(function (self, index, arr) {
arr.indexOf(self) === index ? arr2.push(self) : null;
});
console.log(arr2);
可以看出,for循环是将整个数组多次遍历,而forEach只需要找到第一次出现的索引进行比对,在数据量大的时候能显著提升效率
对for循环的终止
在js中有break
return
continue
对函数进行中断或跳出循环的操作,我们在 for
循环中会用到一些中断行为,对于优化数组遍历查找是很好的,但forEach是迭代器,只能一次遍历至结束,不可以中途遍历或停止
let arr = [1, 2, 3, 4],
i = 0,
length = arr.length;
for (; i < length; i++) {
console.log(arr[i]); //1,2
if (arr[i] === 2) {
break;
}
}
arr.forEach((self, index) => {
console.log(self);
if (self === 2) {
break; //报错
}
});
arr.forEach((self, index) => {
console.log(self);
if (self === 2) {
continue; //报错
}
});
包括return在内,都不会生效(但是return不会报错)
let arr = [1, 2, 3, 4];
function find(array, num) {
//声明一个find函数,接收两个参数,一个是数组,一个是要查找的值
array.forEach((self, index) => {
//遍历数组
if (self === num) {
//如果数组中的值等于要查找的值
return index; //返回该值的索引
}
});
}
let index = find(arr, 2);
console.log(index);// undefined
for循环控制起点
就像上文说的,forEach不可控制终点起点,但for循环可以
let arr = [1, 2, 3, 4],
i = 1,
length = arr.length;
for (; i < length; i++) {
console.log(arr[i]) // 2 3 4
};
而forEach中的index,不论是删除或是更改,都是无法生效的
let arr = [1, 2, 3, 4];
arr.forEach((item, index) => {
index++;
console.log(item); // 1 2 3 4
});
性能比较
性能比较:for > forEach > map
原因:
for:for循环没有额外的函数调用栈和上下文,所以它的实现最为简单。
forEach:对于forEach来说,它的函数签名中包含了参数和上下文,所以性能会低于 for 循环。
map:map 最慢的原因是因为 map 会返回一个新的数组,数组的创建和赋值会导致分配内存空间,因此会带来较大的性能开销。如果将map嵌套在一个循环中,便会带来更多不必要的内存消耗。
总结
- 语法:foreach是一种迭代数组或集合元素的简化语法,而for是一种传统的循环语法。
- 灵活性:foreach比for更灵活,可以在MoveNext()和GetCurrent()方法中编写自定义的代码。
- 循环规则:自定义的类如果实现了IEnumerable接口,就可以使用foreach循环,而不管内部是否有一个真实的数组,并且可以自定义循环的规则。
- 使用场景:从面向对象的原则来看,foreach循环更适于多数情况的使用。而对于某些特殊场景,如需要直接访问数组的索引或需要灵活控制循环条件,使用for循环更为合适。
- 错误检测:在使用foreach遍历一个数组时,如果数组元素的类型与foreach循环变量的类型不匹配,编译时就能检测出来并报错。而for循环在这方面没有这样的检查机制。
- 性能:for循环的性能通常比foreach循环要好,因为foreach循环在执行时需要进行额外的函数调用和上下文考虑。