数组的方法大全

前言

JavaScript 数组的真实力量隐藏在数组的属性和方法中,下面我们来重点介绍下数组的方法。

包括ES6新增的一些方法,但这些方法目前不是所有浏览器都支持,使用前请查阅官方文档的浏览器支持情况。

大纲

目前所有数组方法思维导图整理如下:
思维导图

示例解析

检测数组

1、isArray()

该方法的目的就是确定一个值是否为数组, 而不用考虑在哪个全局执行上下文中创建的:

1
2
3
if(Array.isArray(value)) {
// 操作数组
}

在只有一个全局作用域的情况下,使用instanceof也可以:

1
2
3
if(value instanceof Array) {
// 操作数组
}

迭代器方法

1、keys()、values()、entries()

在ES6中,Array原型上暴露了3个用于检索数组内容的方法

  • keys() 返回数组索引的迭代器
  • values() 返回数组元素的迭代器
  • entries() 返回索引/值对的迭代器
1
2
3
4
5
6
7
8
9
10
const arr = ["foo", "bar"]

// 这些方法返回都是迭代器, 可以通过Array.from()直接转换成数组实例
const aKeys = Array.from(arr.keys())
const aValues = Array.from(arr.values())
const aEntries = Array.from(arr.entries())

console.log(aKeys) // [0,1]
console.log(aValues) // ["foo", "bar"]
console.log(aEntries) // [[0,"foo"],[1, "bar"]]

使用ES6的解构,可以很快的在循环中拆分键值对:

1
2
3
4
5
6
7
8
9
10
11
12
const arr = ["foo", "bar"]

for(const [idx, element] of arr.entries()) {
console.log(idx)
console.log(element)
}

输出:
0
foo
1
bar

复制和填充方法

ES6新增的两个方法,使用这两个方法不会改变数组的大小

1、fill()

可以向一个已有的数组中插入全部或者部分相同的值

1
2
3
4
5
6
7
8
9
10
11
12
13
const zeroes = [0,0,0,0,0]

// 用5填充整个数组
zeroes.fill(5);
console.log(zeroes) // [5,5,5,5,5]

// 用6填充索引大于等于3的元素
zeroes.fill(6, 3)
console.log(zeroes) // [5,5,5,6,6]

// 用7填充索引大于等于1且小于3的元素
zeroes.fill(7, 1, 3)
console.log(zeroes) // [5,7,7,6,6]

2、copyWithin()

会按照指定范围浅复制数组中的部分内容,然后将他们插入到指定索引开始的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
const ints = [1,2,3,4,5,6,7,8]

// 复制索引0开始的内容,插入到索引5开始的位置
ints.copyWithin(5);
console.log(ints) // [1,2,3,4,5,1,2,3]

// 复制索引5开始的内容,插入到索引0开始的位置
ints.copyWithin(0, 5);
console.log(ints) // [1,2,3,4,5,1,2,3]

// 复制索引0开始到索引3结束的内容, 插入到索引4开始的位置
ints.copyWithin(4, 0, 3);
console.log(ints) // [1,2,3,4,1,2,3,3]

小结

fill()copyWithin()相同点:

  • 静默忽略超出数组边界、零长度及方向相反的索引范围
  • 会改变原始数组元素,不改变数组长度

转换方法

1、toStringtoLocaleString()

1
2
3
4
5
6
7
const arr = ['小罗', '小蔡']

const a1 = arr.toString()
const a2 = arr.toLocaleString()

console.log(a1) // 小罗,小蔡
console.log(a2) // 小罗,小蔡

2、join

1
2
3
4
5
6
7
const arr = ['小罗', '❤️', '小蔡']

const a1 = arr.join('')
const a2 = arr.join('#')

console.log(a1) // 小罗❤️小蔡
console.log(a2) // 小罗#❤️#小蔡

3、valueOf()

返回数组本身

1
2
3
4
5
const arr = ['小罗', '❤️', '小蔡']

const a1 = arr.valueOf()

console.log(a1) // ['小罗', '❤️', '小蔡']

栈方法

数组对象可以像栈一样,就是一种限制插入和删除项的数据结构。

栈是一种后进先出(LIFO)的结构,也就是先添加的项先被删除。

数据项的插入(推入,push)和删除(弹出,pop)只在栈的一个地方发生,即栈顶。

1、push()pop()

1
2
3
4
5
6
7
8
const arr = [];
arr.push("red", "green")
arr.push("black")

let item = arr.pop()
console.log(item) // black
console.log(arr.length) // 2

栈方法可以与其他方法一起使用

1
2
3
4
5
6
7
8
9
const arr = ["red", "green"];
arr.push("brown");
arr[3] = "black";

console.log(arr.length) // 4

let item = arr.pop()
console.log(item) // black

队列方法

队列以先进后出(FIFO)形式限制访问。

1、shift()unshift()

使用shift()push(),可以把数组当成队列使用。

1
2
3
4
5
6
7
8
let arr = []
arr.push(1,2)
arr.push(3)

let item = arr.shift() // 取得第一项
console.log(arr) // [2,3]
console.log(item) // 1

使用unshift()pop(),可以在相反方向上模拟队列,即在数组开头添加新数据,在数组末尾取得数据。

1
2
3
4
5
6
7
let arr = []
arr.unshift(1,2)
arr.unshift(3)
console.log(arr) // [3,1,2]

let item = arr.pop() // 取得最后一项
console.log(item) // 2

排序方法

1、reverse()

将数组元素反向排序

1
2
3
4
5
const arr = [1,2,3,4]

arr.reverse()

console.log(arr) // [4,3,2,1]

这个方法很直观, 但不够灵活, 故有了sort()方法

2、sort()

会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。

为此,sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。

即使数组元素都是数字,也会先把数组转换为字符串再比较、排序。 比如:

1
2
3
4
5
const arr = [0,1,5,10,15]

arr.sort()

console.log(arr) // [0, 1, 10, 15, 5]

很明显,上述结果不是我们想要的,为此,sort()方法可以接收一个比较函数, 该函数应返回负值、零值或正值,根据返回的(负、零、正)值对值进行排序(升序排列)。

比较函数接收两个参数a,b; a排在b前面,返回负值;a排在b后面,返回正值;a等于b,返回0。

升序排列

1
2
3
4
5
const arr = [0,1,5,10,15]

arr.sort((a,b) => a < b ? -1 : a > b ? 1 : 0)

console.log(arr) // [0,1,5,10,15]

这样排序结果就正常了

降序排列

1
2
3
4
5
const arr = [0,1,5,10,15]

arr.sort((a,b) => a < b ? 1 : a > b ? -1 : 0)

console.log(arr) // [15,10,5,1,0]

若数组元素是树值,或是其valueOf()方法返回数值的对象(如Date对象),比较函数可以写的更简单,如下:

1
2
3
4
5
6
7
8
9
// 降序
function compare(a,b) {
return b - a
}

// 升序
function compare(a,b) {
return a - b
}

操作方法

1、concat()

通过合并(连接)现有数组来创建一个新数组

1
2
3
4
5
6
let arr = [1,2,3,4]

let arr1 = arr.concat(5, [6,7])

console.log(arr) // [1,2,3,4]
console.log(arr1) // [1,2,3,4,5,6,7]

打平数组参数的行为可以重写,方法是在参数数组上指定宇哥特殊的符号: Symbol.isConcatSpreadable。 这个符号可以阻止concat()打平参数数组,设置true可以强制打平类数组对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let arr = [1,2,3,4]

let newArr = [5,6]
let moreNewArr = {
[Symbol.isConcatSpreadable]: true,
length: 2,
0: "pink",
1: "red"
}

newArr[Symbol.isConcatSpreadable] = false

// 强制不打平数组
let arr2 = arr.concat("red", newArr)

// 强制打平类数组
let arr3 = arr.concat(moreNewArr)

console.log(arr2) // [1,2,3,4,"red", [5,6]]
console.log(arr3) // [1,2,3,4,"pink","red"]

2、slice()

用于创建一个包含原有数组中一个或多个元素的新数组。

可接收一个或两个参数,若只有一个参数,则返回该索引到数组末尾的所有元素; 若有两个参数,返回从开始索引到结束索引对应的所有元素(其中不包括结束索引对应的元素)

1
2
3
4
5
6
7
8
9
10
11
let arr = [1,2,3,4]

let arr1 = arr.slice(1)
let arr2 = arr.slice(1,3)

// 若参数是负数, 可以用数组长度+这个负值确定数组位置
let arr3 = arr.slice(-2,-1) // 相当于调用slice(2,3)

console.log(arr1) // [2,3,4]
console.log(arr2) // [2,3]
console.log(arr3) // [3]

3、splice()

或许最强大的数组方法就是splice(), 使用它的方式有很多种。 它的主要目的是在数组中间插入元素,但有3中不同的方式使用了这个方法:

3.1、删除

需要给splice()传递2个参数: 要删除的第一个元素的位置和要删除的元素数量。

1
2
3
4
5
let arr = [1,2,3,4]
let removed = arr.splice(0,2) // 删除前两项

console.log(arr) // [3,4]
console.log(removed) // [1,2]
3.2、插入

需要给splice()传递3个参数: 开始位置(该位置元素在插入后被挤到后面)、 0(要删除的元素数量)、要插入的元素。

1
2
3
4
5
let arr = [1,2,3,4]
let removed = arr.splice(1,0,"red","blue") // 在位置1插入两个元素

console.log(arr) // [1,“red”, "blue", 2,3,4]
console.log(removed) // 空数组
3.3、替换

需要给splice()传递3个参数: 开始位置、要删除的元素数量、要插入的任意多个元素。

要删除的元素数量不一定和要插入的元素数量一致。

1
2
3
4
5
let arr = [1,2,3,4]
let removed = arr.splice(1,1,"red","blue") // 在位置1插入两个元素

console.log(arr) // [1,“red”, "blue",3,4]
console.log(removed) // [2]

搜索和位置方法

严格相等的搜索方法

1、indexOf()lastIndexOf()

这两个方法都返回要查找的元素在数组中的位置,若没找到返回-1

2、includes()

该方法返回布尔值,表示是否至少找到一个与指定元素匹配的项。 若找到返回true,未找到返回false

1
2
3
4
5
6
7
8
9
10
11
12
13
const mudanPerson = ['毛毛', '乐乐', '蔡蔡', 'jeff', '小罗', '蔡蔡']

// 检索姓名是蔡蔡的, 在数组中的位置
const firstIndex = mudanPerson.indexOf('蔡蔡')
const lastIndex = mudanPerson.lastIndexOf('蔡蔡')

const isInclude = mudanPerson.includes('蔡蔡')


output:
firstIndex ---> 2 // 返回查找到的第一个元素所在的位置
lastIndex ---> 5 // 返回查找到的最后一个元素所在的位置
isInclude ---> true // 找到匹配项了

断言函数搜索数组

1、find()findIndex()

这两个方法都是从最小索引开始匹配,每个索引都会调用这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const mudanPerson = [
{name: '小罗', age: 24, birthday: 1998, feature: '母单', isPretty: '是'},
{name: '毛毛', age: 22, birthday: 2000, feature: '母单', isPretty: '是'}, {name: 'Jeff', age: 25, birthday: 1997, feature: '母单', isPretty: '是'}, {name: '乐乐', age: 26, birthday: 1996, feature: '非母单',isPretty: '是'},
{name: '蔡蔡', age: 27, birthday: 1995, feature: '母单', isPretty: '是'},{name: '文婷', age: 24, birthday: 1998, feature: '母单', isPretty: '是'},{name: '烨子', age: 24, birthday: 1998, feature: '母单', isPretty: '是'},{name: '奇奇', age: 27, birthday: 1995, feature: '非母单',isPretty: '是'},
{name: '团团', age: 22, birthday: 2000, feature: '母单', isPretty: '是'}]

// 返回第一个匹配的元素
const result = mudanPerson.find(item => {
return item.age > 26
})

// 返回第一个匹配元素的索引
const index = mudanPerson.findIndex(item => {
return item.age > 26
})

console.log(result) // {name: '蔡蔡', age: 27, birthday: 1995, feature: '母单', isPretty: '是'}

console.log(index) // 4

迭代方法

这5个迭代方法都接收两个参数: 以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中this的值)。 每个方法的函数都接收3个参数: 当前数组元素、当前元素索引、数组本身。

1、forEach()

对数组的每一项都运行传入的函数, 无返回值

1
2
3
4
5
6
7
8
9
10

const arr = ['毛毛', '乐乐', 'jeff', '小罗']

let temp = []
arr.forEach((item,index,arr) => {
// 代码
temp.push({name: item})
})

console.log(temp) // [{"name":"毛毛"},{"name":"乐乐"},{"name":"jeff"},{"name":"小罗"}]

2、map()

对数组的每一项都运行传入的函数, 返回由每次函数调用的结果构成的数组。

1
2
3
4
5
6
7
8
9
10

const arr = ['毛毛', '乐乐', 'jeff', '小罗']
var brr = arr.map((item,index,arr) => {
// 代码
return '名字:' + item;
})

// map的用法和forEach差不多。但是map是有返回值的。他的返回值是一个新数组

console.log(brr) // ['名字:毛毛', '名字:乐乐', '名字:jeff', '名字:小罗']

3、filter()

对数组的每一项都运行传入的函数, 函数返回true的项会组成数组返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const person = [
{name: '小罗', age: 24, birthday: 1998},
{name: '毛毛', age: 22, birthday: 2000},
{name: 'Jeff', age: 25, birthday: 1997},
{name: '乐乐', age: 26, birthday: 1996},
{name: '蔡蔡', age: 27, birthday: 1995},
{name: '文婷', age: 24, birthday: 1998},
{name: '烨子', age: 24, birthday: 1998},
{name: '奇奇', age: 27, birthday: 1995},
{name: '团团', age: 22, birthday: 2000}]

// 筛选出年龄小于等于24岁的人员
const filterArr = person.filter(item => {
return item.age <= 24
})

console.log(filterArr)
// [{"name":"小罗","age":24,"birthday":1998},{"name":"毛毛","age":22,"birthday":2000},{"name":"文婷","age":24,"birthday":1998},{"name":"烨子","age":24,"birthday":1998},{"name":"团团","age":22,"birthday":2000}]

4、every()

对数组的每一项都运行传入的函数, 如果对每一项函数都返回true,则返回true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const mudanPerson = [
{name: '小罗', age: 24, birthday: 1998},
{name: '毛毛', age: 22, birthday: 2000},
{name: 'Jeff', age: 25, birthday: 1997},
{name: '乐乐', age: 26, birthday: 1996},
{name: '蔡蔡', age: 27, birthday: 1995},
{name: '文婷', age: 24, birthday: 1998},
{name: '烨子', age: 24, birthday: 1998},
{name: '奇奇', age: 27, birthday: 1995},
{name: '团团', age: 22, birthday: 2000}]

// 判断人员年龄是否都大于等于22, 若都是, 则返回true, 若有一个不是则返回false
const result = mudanPerson.every(item => {
return item.age >= 22
})

console.log(result) // true

5、some()

对数组的每一项都运行传入的函数, 若有一项函数返回true, 则返回true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const mudanPerson = [
{name: '小罗', age: 24, birthday: 1998},
{name: '毛毛', age: 22, birthday: 2000},
{name: 'Jeff', age: 25, birthday: 1997},
{name: '乐乐', age: 26, birthday: 1996},
{name: '蔡蔡', age: 27, birthday: 1995},
{name: '文婷', age: 24, birthday: 1998},
{name: '烨子', age: 24, birthday: 1998},
{name: '奇奇', age: 27, birthday: 1995},
{name: '团团', age: 22, birthday: 2000}]

// 判断是否有人出生年份在1995, 若有一项有则返回true, 全都没有则返回false
const result = mudanPerson.some(item => {
return item.birthday === 1995
})

console.log(result) // true

归并方法

1、reduce()reduceRight()

这两个方法都接受两个参数: 对每一项都会运行的归并函数和可选的归并起点的初始值

归并函数接收4个参数: 上一个归并值、当前项、当前项的索引、数组本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const mudanPerson = [
{name: '小罗', age: 24, birthday: 1998},
{name: '毛毛', age: 22, birthday: 2000},
{name: 'Jeff', age: 25, birthday: 1997},
{name: '乐乐', age: 26, birthday: 1996},
{name: '蔡蔡', age: 27, birthday: 1995},
{name: '文婷', age: 24, birthday: 1998},
{name: '烨子', age: 24, birthday: 1998},
{name: '奇奇', age: 27, birthday: 1995},
{name: '团团', age: 22, birthday: 2000}]

// 计算所有人的年龄总和
const ageTotal = mudanPerson.reduce((total, cur) => {
return total + cur.age
}, 0)

const ageTotal1 = mudanPerson.reduceRight((total, cur) => {
return total + cur.age
}, 0)

// 这两种方法计算结果一样, 只是计算顺序不同, reduce从左往右计算, reduceRight从右往左计算

console.log(ageTotal) // 221
console.log(ageTotal1) // 221

“拉平”方法

1、flat()

数组的成员有时还是数组,flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组, 对原数组不改变。

flat()默认只会“拉平”一层,若想要拉平多层, 可以给flat()传一个整数参数, 表示要拉平的层数。

1
2
3
4
5
6
7
8
9
10
const arr = [1,2,[3,4, [5,6]]]

// 默认拉平一层
const arr1 = arr.flat()

// 拉平两层
const arr2 = arr.flat(2)

console.log(arr1) // [1,2,3,4,[5,6]]
console.log(arr2) // [1,2,3,4,5,6]

如果不管多少层嵌套, 都要转成一维数组, 可以用Infinity关键字作为参数。

1
2
3
4
5
const arr = [1,[2,[3,[4,[5,6,[7,8]]]]]]

const arr1 = arr.flat(Infinity)

console.log(arr1) // [1,2,3,4,5,6,7,8]

如果原数组有空位,flat()方法会跳过空位。

1
2
3
4
const arr = [1,2,,4,5]

const arr1 = arr.flat()
console.log(arr1) // [1,2,4,5]

2、flatMap()

flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。

1
2
3
4
5

// 相当于[[2,4],[3,6],[4,8]].flat()
const arr = [2,3,4].flatMap(x => [x, x*2])

console.log(arr) // [2, 4, 3, 6, 4, 8]

flatMap()只能展开一层数组

1
2
3
4
5

// [[[1]], [[4]], [[6]], [[8]]].flat()
const arr = [1,2,3,4].flatMap(x => [[x*2]])

console.log(arr) // [[1],[4],[6],[8]]

flatMap()方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。

1
2
3
arr.flatMap(function callback(currentValue[, index[, array]]) {
// ...
}[, thisArg])

flatMap()方法还可以有第二个参数,用来绑定遍历函数里面的this

文章作者: candy
文章链接: https://github.com/candy415/2022/11/07/数组的方法大全/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 candy的小窝