忙了好一段时间,项目上线后终于有那么一点点空档期静下来整理一些问题了。当我们在开发项目的时候,用到遍历的地方肯定少不了,那么我们有那么多的遍历方法,在不同情况下用那种方法会更优雅而且还没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循环解决了这个问题。

  换了一种遍历方法后,这个问题就解决了,在这里简单的做了一些开发时遇到的坑做了一些总结,有不同看法的各路大神,跪求分享!!!

简单了解JS中的几种遍历的更多相关文章

  1. JS中的五种去重方法

    JS中的五种去重方法 第一种方法: 第二种方法:  第三种方法: 第四种方法: 第五种方法:优化遍历数组法 思路:获取没重复的最右一值放入新数组 * 方法的实现代码相当酷炫,* 实现思路:获取没重复的 ...

  2. js中的几种继承方法

    JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一. 继承:子承父业:一个原本没有某些方法或属性的对象,统一写方法,拿到了另外一个对象的属性和方法 下面是js中的几种继承方式 1.改变this ...

  3. 研究分析JS中的三种逻辑语句

    JS中的三种逻辑语句:顺序.分支和循环语句. 一.顺序语句 代码规范如下:1. <script type="text/javascript"> var a = 10;  ...

  4. js中的三种函数写法

    js中的三种函数写法 <script type="text/javascript"> //普通的声明方式 function myFun(m,n){ alert(m+n) ...

  5. 实现一个函数clone,可以对JS中的5种数据类型(Number、String、Object、Array、Boolean)进行值复制

     实现一个函数clone,可以对JS中的5种数据类型(Number.String.Object.Array.Boolean)进行值复制

  6. 简单谈谈Python中的几种常见的数据类型

    简单谈谈Python中的几种常见的数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等 ...

  7. 【js实例】js中的5种基本数据类型和9种操作符

    js中的5中基本数据类型 js标识符 第一个字符必须为字母,下划线,或美元符 其他字符可以是字母,下划线,美元符,数字 js标识符区分大小写 标识符不能使关键字和保留字 关键字: break do i ...

  8. 简单谈谈js中的MVC

    MVC是什么? MVC是一种架构模式,它将应用抽象为3个部分:模型(数据).视图.控制器(分发器). 本文将用一个经典的例子todoList来展开(代码在最后). 一个事件发生的过程(通信单向流动): ...

  9. 简单聊一聊JS中的循环引用及问题

    本文主要从 JS 中为什么会出现循环引用,垃圾回收策略中引用计数为什么有很大的问题,以及循环引用时的对象在使用 JSON.stringify 时为什么会报错,怎样解决这个问题简单谈谈自己的一些理解. ...

随机推荐

  1. Android View架构总结

    View和Activity的区别 android的四大组件,Activity是四大组件中唯一一个用来和用户进行交互的组件.可以说Activity就是android的视图层. 如果再细化,Activit ...

  2. 11 PopupMenu菜单和代码例子

    PopupMenu 弹出式菜单 API 11以上可用 1. 获取弹出菜单的对象 2. 在res里的menu添加菜单项 3. 将布局里的菜单项 给弹出菜单 4. 进行监听弹出菜单 5. 展示出弹出菜单 ...

  3. UNIX环境高级编程——线程属性

    pthread_attr_t 的缺省属性值 属性 值 结果 scope PTHREAD_SCOPE_PROCESS 新线程与进程中的其他线程发生竞争. detachstate PTHREAD_CREA ...

  4. USB OTG原理+ ID 检测原理

    OTG 检测的原理是: USB OTG标准在完全兼容USB2.0标准的基础上,增添了电源管理(节省功耗)功能,它允许设备既可作为主机,也可作为外设操作(两用OTG).USB OTG技术可实现没有主机时 ...

  5. Jeff Atwood:软件工程已死?

    原文作者:Jeff Atwood 2009年7月,Tom DeMarco在<IEEE Software>杂志上发表了一篇论文,题为"Software Engineering: A ...

  6. Java学习从菜鸟变大鸟之二 输入输出流(IO)

    在软件开发中,数据流和数据库操作占据了一个很重要的位置,所以,熟悉操作数据流和数据库,对于每一个开发者来说都是很重要的,今天就来总结一下JavaI/O. 流 流是一个很形象的概念,当程序需要读取数据的 ...

  7. android studio中使用lambda

    环境准备 如果还没有安装Java 8,那么你应该先安装才能使用lambda和stream(译者建议在虚拟机中安装,测试使用). 像NetBeans 和IntelliJ IDEA 一类的工具和IDE就支 ...

  8. SwiftyiRate中文说明

    SwiftyiRate Github SwiftyiRate Swift语言实现的app内评分,简单易用. Requirements Integration Usage Initialization ...

  9. (NO.00001)iOS游戏SpeedBoy Lite成形记(十六)

    接上篇,我们实现菜单窗口的弹出和关闭功能,首先在打开GameScene.m,添加必要的实例变量: __weak PopupLayer *_popupLayer; 再添加2个新方法: -(void)re ...

  10. 【一天一道LeetCode】#53. Maximum Subarray

    一天一道LeetCode系列 (一)题目 Find the contiguous subarray within an array (containing at least one number) w ...