前言
JavaScript数组的迭代方法,前端开发人员想必都不陌生了,在开发中基本上都会用上其中的一种或是几种,说起迭代方法,可能很多人首先想到的就是for循环,我之前遍历数组的时候用的最多的就是for,但是在开始使用的迭代方法之后,就很少使用for循环进行数组操作了,我最常用的是 map和foreach ,也会使用every和some,如果使用得当,不仅可以减少代码冗余,提升可读性和可维护性,也可以在一定程度上优化性能。
es5新增了几个数组的迭代方法(every,filter,forEach,map,some),好了,废话不多说,下面开始进入到我们的正题,介绍各个方法的使用情况。
参数:这些方法都接收两个参数callback是数组每一项执行的那个指定函数,使用三个参数,thisArg(可选,执行 callback 函数时 使用的this 值)callback参数如下
- currentValue,第一个参数,数组中正在处理的当前元素
- index,第二个参数,数组中正在处理的当前元素的索引 (可选)
- array,方法被调用的数组(可选)
1.map方法
为数组每一项执行一个指定函数,返回每次函数调用的结果组成的数组。这个是我使用的最多的,eg: 在react中循环输出组件
??? 试想一下,现在有一个需求,给定一个数组,然后给数组里面的每一项加1
我们先来看下for循环是怎么实现的1
2
3
4let array = [3, 6, 12, 33, 52]
for(let i = 0; i < array.length; i++) {
array[i] += 1
}
map方法实现1
array = array.map(function(item) {return item + 1})
map的es6写法1
array = array.map(item => item + 1)
2.forEach方法
forEach()对数组中的每一项运行给定函数 ;简单点来说,本质上跟for没有区别,只是写法不一样。 它的使用也和map类似,参数也类似,只是这个方法没有返回值。这个方法比较简单,只说一些注意的点吧:
- forEach和map一样,如果数组在迭代时被修改了,则其他元素会被跳过,看下面一个例子
1
2
3
4
5
6
7
8
9
10let words = ["one", "two", "three", "four"];
words.forEach((word) => {
console.log(word);
if (word === "two") {
words.shift();
}
});
// one
// two
// four
当word=“two”时,删掉数组的第一个元素,那么数组的每一项都会往前移动一个位置,因为元素 “four”现在在数组更前的位置,”three”会被跳过。 forEach()不会在迭代之前创建数组的副本。
- forEach没有办法中止或者跳出循环,除非抛出一个异常。
3.filter方法
filter():对数组中的每一项运行给定函数。该函数会返回true的项组成的数组。
筛选出分数大于60分的同学。
1 | let stu = [ |
还有一个应用是数组去重,用filter也是比较容易实现的。eg:
先来看看for循环是怎么实现的1
2
3
4
5
6
7var newarr = [],
arr = ['hello', 'hi', 'world', 'hello', 'apple', 'world', 'dream']
for(var i = 0,len = arr.length;i < len; i++){
if(newarr.indexOf(arr[i])===-1){
newarr.push(arr[i]);
}
}
再来对比一下filter,是不是精简了很多。1
2
3newarr = arr.filter((item,index,self) => {
return self.indexOf(item) == index
})
4.Every和Some
因为这两个方法的使用场景很像,所以就一起写了。
every()对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true;
some()对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true;
every相当于并且,some相当于或
检测数组中的所有元素是否都大于 10
1 | let arr = [12, 5, 8, 13, 44] |
检测在数组中是否有元素大于 10
1 | passed = arr.some((item) => item > 10) |
补充 (es6新增的数组迭代方法)
1.for-of
介绍for of之前,先来看看什么是Iterator(遍历器)以下摘至es6标准入门的Iterator,只有一部分定义,要了解更多的可以去看看可以去看阮大神的es6标准入门
- JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
- 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
- Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。
- 那么问题来了,一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。也就是说,for…of循环内部调用的是数据结构的Symbol.iterator方法。
- for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。
1
2
3
4
5
6
7
8
9
10
11
12const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let v of obj) {
console.log(v); // red green blue
}
空对象obj部署了数组arr的Symbol.iterator属性,结果obj的for…of循环,产生了与arr完全一样的结果。
for in 循环是获得键值,for of 允许遍历获得值1
2
3
4
5
6
7
8
9var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
for…of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。1
2
3
4
5
6let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i of arr) {
console.log(i); // "3", "5", "7"
}