前言
JavaScript 数组的真实力量隐藏在数组的属性和方法中,下面我们来重点介绍下数组的方法。
包括ES6新增的一些方法,但这些方法目前不是所有浏览器都支持,使用前请查阅它的浏览器支持情况。
大纲
目前所有数组方法思维导图整理如下:
示例解析
检测数组
1、isArray()
该方法的目的就是确定一个值是否为数组, 而不用考虑在哪个全局执行上下文中创建的:
if(Array.isArray(value)) {
// 操作数组
}
在只有一个全局作用域的情况下,使用instanceof也可以:
if(value instanceof Array) {
// 操作数组
}
迭代器方法
1、keys()、values()、entries()
在ES6中,Array原型上暴露了3个用于检索数组内容的方法
keys()返回数组索引的迭代器values()返回数组元素的迭代器entries()返回索引/值对的迭代器
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的解构,可以很快的在循环中拆分键值对:
const arr = ["foo", "bar"]
for(const [idx, element] of arr.entries()) {
console.log(idx)
console.log(element)
}
输出:
0
foo
1
bar
复制和填充方法
ES6新增的两个方法,使用这两个方法不会改变数组的大小
1、fill()
可以向一个已有的数组中插入全部或者部分相同的值
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()
会按照指定范围浅复制数组中的部分内容,然后将他们插入到指定索引开始的位置
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、toString与toLocaleString()
const arr = ['小罗', '小蔡']
const a1 = arr.toString()
const a2 = arr.toLocaleString()
console.log(a1) // 小罗,小蔡
console.log(a2) // 小罗,小蔡
2、join
const arr = ['小罗', '❤️', '小蔡']
const a1 = arr.join('')
const a2 = arr.join('#')
console.log(a1) // 小罗❤️小蔡
console.log(a2) // 小罗#❤️#小蔡
3、valueOf()
返回数组本身
const arr = ['小罗', '❤️', '小蔡']
const a1 = arr.valueOf()
console.log(a1) // ['小罗', '❤️', '小蔡']
栈方法
数组对象可以像栈一样,就是一种限制插入和删除项的数据结构。
栈是一种后进先出(LIFO)的结构,也就是先添加的项先被删除。
数据项的插入(推入,push)和删除(弹出,pop)只在栈的一个地方发生,即栈顶。
1、push()与pop()
const arr = [];
arr.push("red", "green")
arr.push("black")
let item = arr.pop()
console.log(item) // black
console.log(arr.length) // 2
栈方法可以与其他方法一起使用
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(),可以把数组当成队列使用。
let arr = []
arr.push(1,2)
arr.push(3)
let item = arr.shift() // 取得第一项
console.log(arr) // [2,3]
console.log(item) // 1
使用unshift()和pop(),可以在相反方向上模拟队列,即在数组开头添加新数据,在数组末尾取得数据。
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()
将数组元素反向排序
const arr = [1,2,3,4]
arr.reverse()
console.log(arr) // [4,3,2,1]
这个方法很直观, 但不够灵活, 故有了sort()方法
2、sort()
会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。
为此,sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。
即使数组元素都是数字,也会先把数组转换为字符串再比较、排序。 比如:
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。
升序排列
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]
这样排序结果就正常了
降序排列
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对象),比较函数可以写的更简单,如下:
// 降序
function compare(a,b) {
return b - a
}
// 升序
function compare(a,b) {
return a - b
}
操作方法
1、concat()
通过合并(连接)现有数组来创建一个新数组
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可以强制打平类数组对象:
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()
用于创建一个包含原有数组中一个或多个元素的新数组。
可接收一个或两个参数,若只有一个参数,则返回该索引到数组末尾的所有元素; 若有两个参数,返回从开始索引到结束索引对应的所有元素(其中不包括结束索引对应的元素)
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个参数: 要删除的第一个元素的位置和要删除的元素数量。
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(要删除的元素数量)、要插入的元素。
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个参数: 开始位置、要删除的元素数量、要插入的任意多个元素。
要删除的元素数量不一定和要插入的元素数量一致。
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
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()
这两个方法都是从最小索引开始匹配,每个索引都会调用这个函数
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()
对数组的每一项都运行传入的函数, 无返回值
const arr = ['毛毛', '乐乐', 'jeff', '小罗']
let temp = []
arr.forEach((item,index,arr) => {
// 代码
temp.push({name: item})
})
console.log(temp) // [{"name":"毛毛"},{"name":"乐乐"},{"name":"jeff"},{"name":"小罗"}]
2、map()
对数组的每一项都运行传入的函数, 返回由每次函数调用的结果构成的数组。
const arr = ['毛毛', '乐乐', 'jeff', '小罗']
var brr = arr.map((item,index,arr) => {
// 代码
return '名字:' + item;
})
// map的用法和forEach差不多。但是map是有返回值的。他的返回值是一个新数组
console.log(brr) // ['名字:毛毛', '名字:乐乐', '名字:jeff', '名字:小罗']
3、filter()
对数组的每一项都运行传入的函数, 函数返回true的项会组成数组返回。
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
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
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个参数: 上一个归并值、当前项、当前项的索引、数组本身
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()传一个整数参数, 表示要拉平的层数。
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关键字作为参数。
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()方法会跳过空位。
const arr = [1,2,,4,5]
const arr1 = arr.flat()
console.log(arr1) // [1,2,4,5]
2、flatMap()
flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
// 相当于[[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]], [[4]], [[6]], [[8]]].flat()
const arr = [1,2,3,4].flatMap(x => [[x*2]])
console.log(arr) // [[1],[4],[6],[8]]
flatMap()方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。
arr.flatMap(function callback(currentValue[, index[, array]]) {
// ...
}[, thisArg])
flatMap()方法还可以有第二个参数,用来绑定遍历函数里面的this。