简单了解JS中的几种遍历
忙了好一段时间,项目上线后终于有那么一点点空档期静下来整理一些问题了。当我们在开发项目的时候,用到遍历的地方肯定少不了,那么我们有那么多的遍历方法,在不同情况下用那种方法会更优雅而且还没bug呢?
首先,我在这里先列出几种常见的遍历机制,然后针对部分来做一个我对它的理解,有不同看法的老铁也可以分享一下,下面是我列出来的几种遍历的方法,另外我们常用来中断循环的语句我在这里简单的提一下:
a、continue: 中断本次循环;
b、return和break直接跳出循环。
//
for
var arr = [1, 2, 3
]
for(
var i = 0; i < arr.length; i++
) {
//
do something
};
//
for...of...
for(
var
i of arr) {
//
do something
};
//
for...in..
for(
var i
in
arr) {
//
do something
};
//
forEach() arr.forEach((item, index, arr) =>
{
//
do something}); });
//
map() arr.map((value,index,array) =>
{
//
do something });
在开发上一个项目的时候,我在用for...in...这个方法遍历的时候就遇到一个诡异的bug,在android手机是完美的展示,但是在iPhone手机的时候,就出现了遍历后的数据竟然是翻倍的,而且数据是重复的。然后我就想着用array.forEach的方法,来解决问题总该行来吧,在正常逻辑的情况下是可以解决问题的,但是,在稍微复杂的逻辑了,有时候我们要中断forEach遍历时,这时候就会显得软弱无力了,因为在forEach的遍历机制里是不支持中断遍历的,然后我只能寻求其他的解决方案了。
下面我在这对上述列举的遍历方法逐个逐个的分析一下,有些分析不到位的,或者有不同的看法的老铁们请分享你的看法:
1. 普通的for循环
var arr = [1, 2, 3
]
for(
var i = 0; i < arr.length; i++
) { // 这里的i是代表数组的下标 console.log(i);
//
0, 1, 2 };
最简单的一种,正常用的话也不会出现什么问题,想中断也可以中断,性能上也还可以。

2. 优化版的for循环
var arr = [1, 2, 3
]
for(
var i = 0, len = arr.length; i < len; i++
) { // 这里的i是代表数组的下标
console.log(i);
//
0, 1, 2 };
使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显。这种方法基本上是所有循环遍历方法中性能最高的一种,并且这一类型的for循环可以通过用break来中断循环,如下图所示:

3. for...of...遍历(这种遍历支持ES6)
var arr = [1, 2, 3
]
for(
var item of arr) {
//
item代表数组里面的元素 console.log(item);
//
1, 2, 3 };
1、 这是最简洁、最直接的遍历数组元素的语法
2、 这个方法避开了for-in循环的所有缺陷
3、 与forEach()不同的是,它可以正确响应break、continue和return语句
4、性能要好于forin,但仍然比不上普通for循环

4. forEach()
var arr = [1, 2, 3
]; arr.forEach((item, index, arr) => {
//
item为arr的元素,index为下标,arr原数组 console.log(item);
//
1, 2, 3 console.log(index);
//
0, 1, 2 console.log(arr);
//
[1, 2, 3] });
这种遍历便捷还是挺便捷的,看起来优雅,对目标数组的操作很人性化,要元素给元素,要下标给下标,但是当某种情况你想中断遍历的时候,你就会感觉它就像鸡肋,食之无味,弃之可惜。由于foreach是Array型自带的,对于一些非这种类型的,无法直接使用(如NodeList),所以才有了这个变种,使用这个变种可以让类似的数组拥有foreach功能。而且forEach的性能也会比普通的for循环弱。又下面的例子我们可以看到,我们常用的return false是可以终止代码继续往下执行的,但是在forEach遍历中,并没有终止循环,所以在用forEach的时候,要考虑使用场景了。

5.some()
var arr = [1, 2, 3
]; arr.some((item, index, arr) => {
//
item为数组中的元素,index为下标,arr为目标数组 console.log(item);
//
1, 2, 3 console.log(index);
//
0, 1, 2 console.log(arr);
//
[1, 2, 3] })
some作为一个用来检测数组是否满足一些条件的函数存在,同样是可以用作遍历的函数签名同forEach,有区别的是当任一callback返回值匹配为true则会直接返回true,如果所有的callback匹配均为false,则返回false。
some() 方法会依次执行数组的每个元素:
- 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
- 如果没有满足条件的元素,则返回false。

7. every()
var arr = [1, 2, 3
]; arr.every((item, index, arr) => {
//
item为数组中的元素,index为下标,arr为目标数组 return item > 0;
//
true return index == 0;
//
false })
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
every() 方法使用指定函数检测数组中的所有元素:
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
- 如果所有元素都满足条件,则返回 true。

8. for...in...遍历
var arr = [1, 2, 3
]
for(
var item
in arr) {
//
item遍历数组时为数组的下标,遍历对象时为对象的key值 console.log(item);
//
0, 1, 2 };
for...in更多是用来遍历对象,很少用来遍历数组, 不过 item 对应与数组的 key值,建议不要用该方法来遍历数组,因为它的效率是最低的。

9. filter()
var arr = [1, 2, 3
]; arr.filter(item => {
//
item为数组当前的元素
return item > 1;
//
[2, 3] })
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

10. map()
var arr = [1, 2, 3
]; arr.map(item => {
//
item为数组的元素 console.log(item);
//
1, 2, 3
return item * 2;
//
返回一个处理过的新数组[2, 4, 6] })
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。
这种方式也是用的比较广泛的,虽然用起来比较优雅,但实际效率还比不上foreach

上述简单的介绍了各种遍历的方法。
在前面我有提到一个苹果手机遍历出现数据重复的bug,那么我在这里做一个用 for...in...案例,首先来看看代码:
for (let item
in
this
.currentForm) {
var subFormDataObj =
{};
if (
this.currentForm[item].isRequired === 2 && !
this.currentForm[item].subjectValue &&
this.currentForm[item].subjectType != 5
) {
this.isSubmit =
true
;
this
.hideLoading();
this.Toast('尚有数据未完成,提交失败!'
);
return
false
; }
else
{
if (
this.expressionTest(
this
.currentForm[item].subjectValue)) {
this.Toast('内容不能含有表情符,请重新输入!'
);
this.isSubmit =
true
;
this
.hideLoading();
return
false
; }
if (
this.currentForm[item].subjectName.indexOf('手机号') > -1
) {
if(!
this.checkPhone(
this
.currentForm[item].subjectValue)) {
this.isSubmit =
true
;
this
.hideLoading();
return
false
; }; } subFormDataObj.subjectId =
this
.currentForm[item].subjectId; subFormDataObj.subjectValue =
this
.currentForm[item].subjectValue; subFormDataObj.picture =
this
.currentForm[item].picurl; subFormDataObj.isRequired =
this
.currentForm[item].isRequired;
if (
this.currentForm[item].subjectType == 5
) {
if (
this.listPicStr == ''
) {
this.isSubmit =
true
;
this
.hideLoading();
this.Toast('尚有数据未完成,提交失败!'
);
return
false
; }
else
{
//
subFormDataObj.picture = "data:image/jpeg;base64," + this.listPicStr.replace(/data:image\/jpeg;base64,/g,'') || '';
} } subFormData.push(subFormDataObj); } }; alert(JSON.stringify(subFormData));

我们看一下安卓手机和苹果手机alert出来的数据有什么区别:在截图中我们就可以发现,苹果手机整体遍历了2轮,这样个结果是不是很诡异?是的我也觉得很诡异,怎么来解决呢,很多很学会想到一些方法,比如用数组去重的方法,并且es6的去重方法很优雅(var newArr = Array.from(new Set(subFormData))),或者其他去重的方法,这样做并没有什么不好,比较方法有千千万,喜欢就好。当时因为考虑到里面的逻辑问题,我就直接改了一行代码,用了最普通的方法,因为性能上会有优势,所以我就用了普通的for循环解决了这个问题。
