本文主要总结了 Js 中数组方法中的迭代方法,对于大部分简单的方法则以简单的示例说明功能和使用方法,您还可以参考文末给出的链接再加深学习,里面的介绍也更加地全面,本文样例代码均基于ES6
语法。
迭代方法
,顾名思义就是对数组进行遍历操作的算法,当然大部分方法都不仅仅只是遍历这么简单,例如filter
方法可以添加遍历的筛选条件,此外对于需要传递回调参数的方法,本文都使用了Lambda
语法,所以需要有一些基础,下面就来介绍这些方法:
forEach()
语法:arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
。
该方法对数组的每个元素执行一次给定的函数,返回值为undefined
,大体上有两个参数:一个回调函数,一个可选的thisArg
参数(当执行回调函数 callback
时,用作 this
的值,如果使用Lambda
表达式来传入回调函数参数, thisArg
参数会被忽略,因为箭头函数在词法上绑定了 this
值,因此示例中不展示使用thisArg
,以下方法若也有thisArg
参数,均同此)。而对于回调函数中的参数来说,有三个参数:curentValue
代表正在遍历的值,index
代表正在遍历的值对应的下标,array
代表被遍历的数组,其中后两个参数为可选参数,详细的使用及细节可见链接。
let color = ['red', 'green', 'blue']
let arr = ['a', 'b', 'c']
/**
* output:
* 0: a
* ['a', 'b', 'c' ]
* ['red', 'green', 'blue']
* 1: b
* ['a', 'b', 'c' ]
* ['red', 'green', 'blue']
* 2: c
* ['a', 'b', 'c' ]
* ['red', 'green', 'blue']
*/
arr.forEach((curVal, index, array) => {
console.log(`${
index}: ${
curVal}`)
console.log(array)
// 由于使用了箭头函数,故可直接方法作用域内变量
console.log(color)
})
注意
forEach()
无法进行链式调用,即forEach()
后不能再存在调用,只能存在之前,类似arr.filter().forEach()
则是允许的。此外除了抛出异常以外,没有办法中止或跳出 forEach()
循环,即无法通过break
关键字提前结束循环。
map()
语法:
let new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
该方法会创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值所组成的新数组,返回值为得到的新数组,只要没有在回调函数中修改原数组,原数组内容就保持不变,各个参数及含义同forEach()
中的介绍,详细的使用及细节可见链接。
let arr = [1, 2, 3]
/**
* output: [2, 4, 6]
*
* 返回由原数组每个元素乘2组成的一个新数组
*/
console.log(arr.map(num => num * 2))
/**
* output: [undefined, undefined, 3]
*
* 当某些元素未经处理,则以undefined进行填充
* 就像这里的1和2没有定义返回行为
*/
console.log(arr.map(num => {
if (num > 2) {
return num
}
}))
/**
* output:
* 1 0 [1, 2, 3]
* 2 1 [1, 2, 3]
* 3 2 [1, 2, 3]
*
* map内还可传递系统自带的函数
* 会自动将根据传入函数最多能够传多少参数,来决定
* 是否将curVal、index、array参数全部传递还是部分传递
*/
arr.map(console.log)
/**
* output: [1, NaN, NaN]
*
* 由于parseInt可接收两个参数:string、radix
* 故map会将curVal和index分别传给这两个参数
* 因此无法得到预期的结果
* 关于parseInt的更多信息,可以查看该链接:
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt
*/
console.log(arr.map(parseInt))
filter()
语法:let newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
。
该方法创建一个新数组,其包含通过所提供函数实现的测试进行筛选后的所有元素,返回值为得到的新数组,只要没有在回调函数中修改原数组,原数组内容就保持不变,各个参数及含义同forEach()
中的介绍,详细的使用及细节可见链接。
let arr = [1, 2, 3, 4, 5]
/**
* output: [3, 4, 5]
*
* 返回数组中大于2的元素组成的数组
*/
console.log(arr.filter(num => num > 2))
/**
* output: []
*
* 没有数据满足条件,则返回空数组
*/
console.log(arr.filter(num => num > 6))
reduce()
语法:arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
。
该方法按顺序对数组中的每个元素执行提供的回调函数,返回汇总所有结果后的值。方法内各个参数及含义如下:accumulator
为累加器,是每次调用回调函数的返回值(如果提供了initialValue
,则第一次的值为次),currentValue
为正在处理的元素,如果提供了initialValue
,则第一次的currentValue
的值为数组的第一个元素,否则为数组的第二个元素,index
即为正在处理元素对应的下标,array
即为处理的原数组,initialValue
为提供的默认值,详细的使用及细节可见链接。
let arr = [10, 2, 6]
let maxFun = (acc, cur) => Math.max(acc, cur)
/**
* output: 10
*
* 对数组内的元素循环调用 max 函数得到数组中的最大值
*/
console.log(arr.reduce(maxFun))
let users = [
{
name: '张三',
age: 18
},
{
name: '李四',
age: 16
},
{
name: '王五',
age: 27
}
]
let maxAge = (acc, cur) => Math.max(acc.age, cur.age)
/**
* output: NaN
*
* 可以发现结果并符合我们的预期,这是由于在第一次执行回调函数后
* acc被赋值为第一次使用 max 函数得到的结果18,而非一个user对象
* 因此在第二次调用 acc.age 就成了 18.age 导致了NaN
*/
console.log(users.reduce(maxAge))
/**
* output: { name: '王五', age: 27 }
*
* 因此对于使用reduce来处理对象的某个字段时,
* 我们必须自己在处理中设置返回一个对象
*/
console.log(users.reduce((acc, cur) => {
if (acc.x > cur.x) {
return acc
}
return cur
}))
/**
* output: 27
*
* 当我们需要对一系列对象的某个字段进行处理时,
* 如果并不希望返回对象,则可以先通过map函数处理原数组
* 如下所示,最终就可以对指定字段进行处理并得到预期结果
*/
console.log(users.map(user => user.age).reduce(maxFun))
reduce()
运行机制假如有以下代码:
let arr = [10, 2, 6]
let sumFun = (accumulator, currentValue, currentIndex, array) => accumulator + currentValue
console.log(arr.reduce(sumFun, 29))
对应的运行过程如下(accumulator
在有初始值时则第一次为初始值,否则为数组第一个元素):
callback | accumulator | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
1 | 29 | 10 | 0 | [10, 2, 6] | 39 |
2 | 39 | 2 | 1 | [10, 2, 6] | 41 |
3 | 41 | 6 | 2 | [10, 2, 6] | 47 |
reduceRight()
从名字也很容易看出来这个方法和上一个reduce
的相似,事实也是如此,和reduce
不同的是,该方法会从右到左对每个元素执行回调函数,注意事项同reduce
,这里不再展示代码样例,详细的使用及细节可见链接。
对于reduce
和reduceRight
方法在边界情况下的处理方法,可见下表:
数组内元素数量 | 是否提供 initialValue |
结果 |
---|---|---|
> 1 | 未提供 | accumulator 为数组中(下略)第一个(最后一个)元素currentValue 为(倒数)第二个元素 |
提供 | accumulator 为 initialValue currentValue 为第一个(最后一个)元素 |
|
= 1 | 未提供 | 直接返回数组中的唯一一个元素 |
= 0 | 提供 | 直接返回 initialValue |
未提供 | 抛出 TypeError |
当源数组arr
为空时,会抛出TypeError
,因此建议在使用reduce
时都提供一个默认值initialValue
。
every()
语法:arr.every(callback(element[, index[, array]])[, thisArg])
。
该方法测试一个数组内的所有元素是否都能通过指定函数的测试,返回一个布尔值,当对空数组(或者是所有元素都未初始化的数据)进行测试时,总返回true
,各个参数及含义同forEach()
中的介绍,详细的使用及细节可见链接。
let arr = ['red', 'green', 'blue']
/**
* output: true
*
* 数组中所有的字符串长度均大于等于3,故返回true
*/
console.log(arr.every(str => str.length >= 3))
some()
语法:arr.some(callback(element[, index[, array]])[, thisArg])
。
该方法测试数组中是不是至少有1个元素通过了指定函数的测试,返回一个布尔值,当对空数组(或者是所有元素都未初始化的数据)进行测试时,总返回false
,各个参数及含义同forEach()
中的介绍,详细的使用及细节可见链接。
let arr = ['red', 'green', 'blue']
/**
* output: true
*
* 数组中只要有一个字符串的长度等于5,就返回true
*/
console.log(arr.some(str => str.length == 5))
find()
(ES6)
语法:arr.find(callback[, thisArg])
。
该方法返回数组中满足指定函数的第一个元素的值,不存在则返回 undefined
,详细的使用及细节可见链接。
let users = [
{
name: '张三',
age: 18
},
{
name: '李四',
age: 16
},
{
name: '张三',
age: 27
}
]
/**
* output: { name: '张三', age: 18 }
*
* 返回根据指定条件查找到的第一个满足的元素
*/
console.log(users.find(user => user.name === '张三'))
注意
如果需要找到一个元素的位置或者一个元素是否存在于数组中,应该使用indexOf()
或 includes()
,这两个方法的使用见上一篇。
findIndex()
(ES6)
语法:arr.findIndex(callback[, thisArg])
。
该方法返回数组中满足指定函数的第一个元素的索引,否则返回-1,和indexOf()
不同的是,findIndex()
可以用过指定函数来设置更复杂的查找条件,适合查找非基本类型的数据,具体选择需要看实际情况进行选择,详细的使用及细节可见链接。
let users = [
{
name: '张三',
age: 18
},
{
name: '李四',
age: 16
},
{
name: '李四',
age: 27
}
]
/**
* output: 2
*
* 返回第一个满足年龄为27的元素的下标
*/
console.log(users.findIndex(user => user.age === 27))
keys()
(ES6)
语法:arr.keys()
。
该方法返回一个包含数组中每个索引键的Array Iterator
对象(无法使用map
、forEach
进行迭代),该方法无参,详细的使用及细节可见链接。
let arr = [1, , 3]
/**
* output:
* 0
* 1
* 2
*
* 即使数组中的某些元素还未被初始化,也会包含那些元素的索引
*/
for (let v of arr.keys()) {
console.log(v)
}
values()
(ES6)
语法:arr.values()
。
该方法会返回一个的Array Iterator
对象,该对象包含数组每个索引对应的值(无法使用map
、forEach
进行迭代),该方法无参,详细的使用及细节可见链接。
let arr = [1, , 3]
/**
* output:
* 1
* undefined
* 3
*
* 如果数组中包含未初始化的值,则得到undefined
*/
for (let v of arr.values()) {
console.log(v)
}
Symbol.iterator
语法:arr[Symbol.iterator]()
。
与上述的values()
返回结果相同,更多详细信息请见链接。
let arr = [1, , 3]
/**
* output:
* 1
* undefined
* 3
*
* 和 values 方法效果相同
*/
for (let v of arr[Symbol.iterator]()) {
console.log(v)
}
entries()
(ES6)
语法:arr.entries()
。
该方法返回一个Array Iterator
对象,包含数组中每个索引的键值对(无法使用map
、forEach
进行迭代),该方法无参,详细的使用及细节可见链接。
let arr = ['a', 'b', 'c']
/**
* output:
* [0, 'a']
* [1, 'b']
* [2, 'c']
*
* 对于每一个元素 v 来说,都是由键值对组成的长度为2的数组
*/
for (let v of arr.entries()) {
console.log(v)
}
注意
在以上的遍历方法中,有很多方法都需要指定一个回调函数作为参数。在每一个数组元素都分别执行完回调函数之前,数组的length属性会被缓存在某个地方,所以,如果在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。总之,不要尝试在遍历过程中对原数组进行任何修改,虽然规范对这样的操作进行了详细的定义,但为了可读性和可维护性,请不要这样做。 —— 来自该链接
本文主要介绍了 Js 中的数组方法剩下的迭代方法,其中多半都是ES6新增的方法,以上便是本文的全部内容,如有错误请您指出,希望能得到您的指正,Js 数组方法的全部总结也到此为止了。