写这篇文章之后觉得掘金的排版真的是一言难尽,然后就看到别人的排版使用了 mdnice ,十分漂亮,所以也用了一下,教程是你的掘金文章本可以这么炫(博客美化工具一波带走),体验地址 markdown-nice体验地址, 官网是 mdnice 本来想把这篇文章直接用 mdnice 排版的,但是篇幅太长了,所以只能拆分一下。没法一下接收太多的同学可以点一下下面的链接。
前端开发过程中,数组是我们经常需要用到的,很多小伙伴包括我也是,对数组的方法就知道那么几个,总觉得够用就行,那不够用的时候咋办呢?百度呗。有时候觉得自己是真的在用代码改变世界吗?还是说就是在混日子,真就是在 “搬砖”?今天复盘一下数组的方法,这可能是比较全面的了。
1. 数组的增删改查
push()
push方法主要是向数组的尾部添加一个或者多个元素,然后返回新数组的长度。
let arr = []
let arrLen = arr.push(1,2,3,4)
console.log("返回数组的长度:", arrLen, ",新数组: ", arr)
// 返回数组的长度: 4 ,新数组: [ 1, 2, 3, 4 ]
pop()
pop方法主要是删除数组的最后一个元素,并返回删除的元素。
let arr = [1,2,3,5]
let deleteItem = arr.pop()
console.log("返回删除的元素: ", deleteItem, ",新数组:", arr)
// 返回删除的元素: 5 ,新数组: [ 1, 2, 3 ]
unshift()
unshift方法可向数组的头部添加一个或多个元素,并返回新数组的长度。
let arr = []
let arrLen = arr.unshift(1,2,3,4)
console.log("返回数组的长度:", arrLen, ",新数组: ", arr)
// 返回数组的长度: 4 ,新数组: [ 1, 2, 3, 4 ]
shift()
shift 方法主要是删除数组的第一个元素,并返回删除的元素。
let arr = [1,2,3,4]
let deleteItem = arr.shift()
console.log("返回删除的元素:", deleteItem, ",新数组:", arr)
// 返回删除的元素: 1 ,新数组: [ 2, 3, 4 ]
splice()
splice方法可以传入多个参数,第一个参数代表数组添加/删除项目的起始下标,第二个参数代表要删除的元素的数量,第 n+2 个参数代表即将向数组添加的元素。以下将使用splice方法对数组实现增删改。splice方法的返回值是一个数组,数组里存放的是删除的元素
使用splice删除数组元素
let arr = [1,2,3,4]
let deleteArr = arr.splice(1, 2)
// 以下标为 1/n 的元素开始,删除两个元素console.log("删除元素后的数组:", arr,"删除的元素:", deleteArr)
// 删除元素后的数组: [ 1, 4 ] 删除的元素: [ 2, 3 ]
使用splice修改数组元素
let arr = ["张三", "李四", "王五", "赵六"]
let deleteArr = arr.splice(1, 1, "熊大")
// 以下标为 1/n 的元素开始,删除一个元素,添加另外一个元素
console.log("修改后的数组:", arr,"删除的元素:", deleteArr)
// 修改后的数组: [ '张三', '熊大', '王五', '赵六' ] 删除的元素: [ '李四' ]
使用splice添加数组元素
let arr = ["张三", "李四", "王五", "赵六"]
let deleteArr = arr.splice(1, 0, "熊大")
// 以下标为 1/n 的元素开始,删除 0/n 个元素,在下标为 1/n 的位置添加 1/n 个元素
console.log("修改后的数组:", arr,"删除的元素:", deleteArr)
// 修改后的数组: [ '张三', '熊大', '李四', '王五', '赵六' ] 删除的元素: []
indexOf()
indexOf 方法用于查找数组元素,可以返回某个指定的字符串值在字符串中首次出现的位置。如果数组中有指定的元素值,那么返回元素的下标,如果没有则返回 -1 。该方法有两个参数,第一个参数是要查找的元素,第二个参数是是要查找的起始下标。
let arr = ["张三", "李四", "王五", "赵六", "张三"]
let index1 = arr.indexOf("张三")
let index2 = arr.indexOf('张三', 1) // 以下标为1开始查找元素
let notFind = arr.indexOf('熊二')
console.log("查找的数组:", arr)
console.log("有范围地查找元素:", index1, "无范围地查找元素的:", index2, "未查找到的返回值:", notFind)
// 查找的数组: [ '张三', '李四', '王五', '赵六', '张三' ]
// 有范围地查找元素: 0 无范围地查找元素的: 4 未查找到的返回值: -1
lastIndexOf()
lastIndexOf也是用于查找数组元素,但是,它是从后面往前找,如果数组中有指定的元素值,那么返回元素的下标,如果没有则返回 -1 。该方法有两个参数,第一个参数是要查找的元素,第二个参数是是要查找的起始下标。
let arr = ["张三", "李四", "王五", "赵六", "张三"]
let index1 = arr.lastIndexOf("张三")
let index2 = arr.lastIndexOf('张三', 1) // 以下标为1开始查找元素
let notFind = arr.lastIndexOf('熊二')
console.log("查找的数组:", arr)
console.log("有范围地查找元素:", index1, "无范围地查找元素的:", index2, "未查找到的返回值:", notFind)
// 查找的数组: [ '张三', '李四', '王五', '赵六', '张三' ]
// 有范围地查找元素: 4 无范围地查找元素的: 0 未查找到的返回值: -1
findIndex()
findIndex是用于查找元素的,它的返回值是返回第一个符合条件的元素的下标,如果没有查找到符合的元素,那么返回 -1。它可以传入两个参数,第一个参数是一个回调函数,数组中的每个元素都调用一次回调函数。在回调函数中可以写你要查找元素的条件,当条件成立为true时,返回该元素的下标,没有则返回 -1。第二个参数我们通常不会用到,但是也说一下,它可以传入一个任意类型的值,这个值可以改变第一个参数内部的this指向。
let arr = [1,2,3,4]
let obj = { name: '张三'}
// 第一个参数是一个回调函数,函数的形参有三个 数组元素:val,数组下标:index,原数组:array
// 如果要使用第二个参数,这里的函数不要使用箭头函数,箭头函数的作用大家可以去查,这里就不累述了//
let index = arr.findIndex(function(val, index, array) {
console.log('数组元素:', val, ",元素下标:", index, ', 原数组:', array, ",传入的对象:", this)
return val <= 2
}, obj) // 传入一个对象,那函数内部的this 指向就改变了,即函数内部的环境上下文就改变了console.log('查找到符合条件的元素的下标:', index)
// 数组元素: 1 ,元素下标: 0 , 原数组: [ 1, 2, 3, 4 ] ,传入的对象: { name: '张三' }
// 查找到符合条件的元素的下标: 0
find()
find可以用于查找元素,它的返回值是返回第一个符合条件的元素,如果没有,则返回undefined。它可以传入两个参数,这两个参数的跟findIndex是一样的,这里就不累述了。
let arr = [1,2,3,4]
let obj = { name: '张三'}
// 第一个参数是一个回调函数,函数的形参有三个 数组元素:val,数组下标:index,原数组:array
// 如果要使用第二个参数,这里的函数不要使用箭头函数,箭头函数的作用大家可以去查,这里就不累述了
let index = arr.find(function(val, index, array) {
console.log('数组元素:', val, ",元素下标:", index, ', 原数组:', array, ",传入的对象:", this)
return val <= 2
}, obj) // 传入一个对象,那函数内部的this 指向就改变了,即函数内部的环境上下文就改变了
console.log('查找到符合条件的元素:', index)
// 数组元素: 1 ,元素下标: 0 , 原数组: [ 1, 2, 3, 4 ] ,传入的对象: { name: '张三' }
// 查找到符合条件的元素的下标: 0
includes()
includes用于判断数组是否存在指定的值,它的返回值是一个布尔值,存在就返回true,不存在就返回false。它可以传入两个参数,第一个参数是要查找的元素,第二个参数是要查找的起始位置。
let arr = [1,2,3,4]let isExit = arr.includes(1, 0)
console.log("是否存在指定的元素:", isExit)
// 是否存在指定的元素: true
总结:想记住数组这么多的api是不太现实的,但是一般常用的还是需要记住的,记住诀窍就是增删改查。
数组循环
数组循环有多种方式,在这里我就不比对那种循环之间的性能比较,纯属就说说它们的用法。
forEach()
forEach方法第一个参数传入一个回调函数,回调函数有三个形参,分别是数组元素,元素下表,元素数组,第二个参数可以传入一个任意类型的值,这个值可以改变第一个参数内部的this指向。返回值是 undefined。
let arr = [1, 2, 3, 4]let obj = { name: "张三"}
let res = arr.forEach(function(val, index, arr) {
console.log("数组元素:", val, "元素下标:", index, "原数组:", arr, '改变this指向:', this)
}, obj)
console.log("返回值: ", res)
// 数组元素: 1 元素下标: 0 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 数组元素: 2 元素下标: 1 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 数组元素: 3 元素下标: 2 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 数组元素: 4 元素下标: 3 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 返回值: undefined
map()
map方法传入一个回调函数,回调函数有三个形参,分别是数组元素,元素下表,元素数组,第二个参数可以传入一个任意类型的值,这个值可以改变第一个参数内部的this指向。返回值是一个新的数组。
let arr = [1, 2, 3, 4]let obj = { name: "张三"}
let res = arr.map(function(val, index, arr) {
console.log("数组元素:", val, "元素下标:", index, "原数组:", arr, "改变this指向:", this)
return val + 1
}, obj)
console.log("返回一个新的数组: ", res, "原数组:", arr)
// 数组元素: 1 元素下标: 0 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 数组元素: 2 元素下标: 1 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 数组元素: 3 元素下标: 2 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 数组元素: 4 元素下标: 3 原数组: [ 1, 2, 3, 4 ] 改变this指向: { name: '张三' }
// 返回一个新的数组: [ 2, 3, 4, 5 ] 原数组: [ 1, 2, 3, 4 ]
filter()
filter方法传入一个回调函数,回调函数有三个形参,分别是数组元素,元素下表,元素数组,第二个参数可以传入一个任意类型的值,这个值可以改变第一个参数内部的this指向。这个方法可以用于进行过滤筛选。它的返回值是一个过滤后的数组。
let arr = [1, 2, 3, 4]let obj = { name: "张三"}
let res = arr.filter(function(val, index, arr) {
console.log("数组元素:", val, "元素下标:", index, "原数组:", arr, "改变this的指向:", this)
return val < 2
}, obj)
console.log("返回一个过滤后的数组: ", res, "原数组:", arr)
// 数组元素: 1 元素下标: 0 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 数组元素: 2 元素下标: 1 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 数组元素: 3 元素下标: 2 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 数组元素: 4 元素下标: 3 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 返回一个过滤后的数组: [ 1 ] 原数组: [ 1, 2, 3, 4 ]
every()
every方法传入一个回调函数,回调函数有三个形参,分别是数组元素,元素下表,元素数组,第二个参数可以传入一个任意类型的值,这个值可以改变第一个参数内部的this指向。它的返回值是一个布尔值,如果数组元素都符合回调函数的条件,则返回 true,否则返回false。
let arr = [1, 2, 3, 4]let obj = { name: "张三"}
let res = arr.every(function(val, index, arr) {
console.log("数组元素:", val, "元素下标:", index, "原数组:", arr, "改变this的指向:", this)
return val < 2
}, obj)
console.log("是否数组内部的所有成员都满足回调函数里到条件: ", res, "原数组:", arr)
// 数组元素: 1 元素下标: 0 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 数组元素: 2 元素下标: 1 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 是否数组内部的所有成员都满足回调函数里到条件: false 原数组: [ 1, 2, 3, 4 ]
some()
every方法传入一个回调函数,回调函数有三个形参,分别是数组元素,元素下表,元素数组,第二个参数可以传入一个任意类型的值,这个值可以改变第一个参数内部的this指向。它的返回值是一个布尔值,如果数组存在元素符合回调函数的条件,则返回 true,否则返回false。
let arr = [1, 2, 3, 4]
let obj = { name: "张三"}
let res = arr.some(function(val, index, arr) {
console.log("数组元素:", val, "元素下标:", index, "原数组:", arr, "改变this的指向:", this)
return val < 2
}, obj)
console.log("是否数组内部的存在成员满足回调函数里到条件: ", res, "原数组:", arr)
// 数组元素: 1 元素下标: 0 原数组: [ 1, 2, 3, 4 ] 改变this的指向: { name: '张三' }
// 是否数组内部的存在成员满足回调函数里到条件: true 原数组: [ 1, 2, 3, 4 ]
entries()
entries方法不传入任何参数,它返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。可以用for...of... 进行循环
let arr = [1, 2, 3, 4]
for(let [key, value] of arr.entries()) {
console.log("数组下标:", key,"数组元素:", value)
}
// 数组下标: 0 数组元素: 1
// 数组下标: 1 数组元素: 2
// 数组下标: 2 数组元素: 3
// 数组下标: 3 数组元素: 4
keys()
keys方法不传入任何参数,它返回一个数组的迭代对象,该对象包含数组的键(key)。可以用for...of... 进行循环
let arr = [1, 2, 3, 4]
for(let key of arr.keys()) {
console.log("数组下标:", key)
}
// 数组下标: 0
// 数组下标: 1
// 数组下标: 2
// 数组下标: 3
values()
values方法不传入任何参数,它返回一个数组的迭代对象,该对象包含数组的元素值(value)。可以用for...of... 进行循环
let arr = [1, 2, 3, 4]
for(let value of arr.values()) {
console.log("数组元素:", value)
}
// 数组元素: 1
// 数组元素: 2
// 数组元素: 3
// 数组元素: 4
reduce()
reduce方法可以传入两个参数,第一个参数传入的是一个回调函数,回调函数里有四个参数,分别是:total,currentValue, currentIndex, array。
-
total:上一次累加的值
-
currentValue:当前的元素
-
currentIndex:当前元素的索引
-
array:当前元素所属的数组对象。
第二个参数传入的是一个初始值。
let arr = [1, 2, 3, 4, 5, 6];
let res = arr.reduce((preVal, curVal, curIndex, array) => {
console.log('上一次累加的值:', preVal, "当前循环的数组元素的值:", curVal, "数组的下标:", curIndex, "原数组:", array)
return preVal + curVal
}, 10);// 传递给函数的初始值
console.log('数组求和:', res)
// 上一次累加的值: 10 当前循环的数组元素的值: 1 数组的下标: 0 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 11 当前循环的数组元素的值: 2 数组的下标: 1 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 13 当前循环的数组元素的值: 3 数组的下标: 2 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 16 当前循环的数组元素的值: 4 数组的下标: 3 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 20 当前循环的数组元素的值: 5 数组的下标: 4 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 25 当前循环的数组元素的值: 6 数组的下标: 5 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 数组求和: 31
reduceRight()
reduceRight方法的使用跟reduce方法的使用是一样的,只不过从后往前累加,看下面的代码就明白了。
let arr = [1, 2, 3, 4, 5, 6]
let res = arr.reduceRight((preVal, curVal, curIndex, array) => {
console.log('上一次累加的值:', preVal, "当前循环的数组元素的值:", curVal, "数组的下标:", curIndex, "原数组:", array)
return preVal + curVal
}, 10) // 传递给函数的初始值
console.log('数组求和:', res)
// 上一次累加的值: 10 当前循环的数组元素的值: 6 数组的下标: 5 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 16 当前循环的数组元素的值: 5 数组的下标: 4 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 21 当前循环的数组元素的值: 4 数组的下标: 3 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 25 当前循环的数组元素的值: 3 数组的下标: 2 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 28 当前循环的数组元素的值: 2 数组的下标: 1 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 上一次累加的值: 30 当前循环的数组元素的值: 1 数组的下标: 0 原数组: [ 1, 2, 3, 4, 5, 6 ]
// 数组求和: 31
数组的其他常用api
reverse()
reverse方法的作用是颠倒数组中元素的顺序,该方法会改变原数组,不会生成新的数组,返回值是翻转后的数组。
let arr = [1, 2, 3, 4, 5, 6]
let res = arr.reverse()
console.log('翻转后的数组:', res, "原数组:", arr)
// 翻转后的数组: [ 6, 5, 4, 3, 2, 1 ] 原数组: [ 6, 5, 4, 3, 2, 1 ]
sort()
sort可以用于数组的排序,能够将数组升序排序和降序排序,该方法会改变原数组,不会生成新的数组,返回值是翻转后的数组。
let arr = [1, 2, 8, 4, 5, 6]
let res1 = arr.sort((a, b) => { // 升序排序
return a - b;
})
console.log("返回值:", res1, "原数组", arr)
let res2 = arr.sort((a, b) => { // 降序排序
return b - a;
})
console.log("返回值:", res2, "原数组", arr)
// 返回值: [ 1, 2, 4, 5, 6, 8 ] 原数组 [ 1, 2, 4, 5, 6, 8 ]
// 返回值: [ 8, 6, 5, 4, 2, 1 ] 原数组 [ 8, 6, 5, 4, 2, 1 ]
concat()
concat 用于拼接元素,该方法可以传任意多个值,可以是具体的值,也可以是数组对象。返回值是一个新的数组。
let arr = [1]
let newArr = arr.concat(2, [3, 4], {name: '张三'}, false, undefined, null,'李四', '')
console.log('拼接后的数组:', newArr, "原数组", arr)
// 拼接后的数组: [ 1, 2, 3, 4, { name: '张三' }, false, undefined, null, '李四', '' ]
// 原数组 [ 1 ]
slice()
slice 可从已有的数组中返回选定的元素。该方法可以传入两个值,两个值都是数组元素的下标值,第一参数表示元素选取的起始位置,第二个参数表示元素选取的结束位置。使用这个方法要记住,包头不包尾,什么意思呢? 看下面的代码。该方法会返回一个子数组。
let arr = [1, 2, 3, 4, 5, 5]
let subArr = arr.slice(1, 3);
console.log('子数组:', subArr, "原来的数组:", arr)
// 子数组: [ 2, 3 ] 原来的数组: [ 1, 2, 3, 4, 5, 5 ]
join()
join方法可以将数组转化为一个字符串,并用指定的字符进行分割。该方法不会影响到原数组,返回值是一个字符创。
let arr = [1, 2, 3, 4, 5, 5]
let strArr = arr.join('--');
console.log('转化的字符串:', strArr, "原来的数组:", arr)
// 转化的字符串: 1--2--3--4--5--5 原来的数组: [ 1, 2, 3, 4, 5, 5 ]
数组的其他不常用api
isArray()
isArray 方法可以判断一个值是否是数组。
let arr = [1, 2, 3, 4, 5, 5]
let isArray = Array.isArray(arr)
console.log('判断是否是数组:', isArray)
// 判断是否是数组: true
- toString 和 toLocaleString
isString 和 toLocaleString 方法可以将一个数组转化为字符串。
let arr = [1, 2, 3, 4, 5, 5]
let strArr1 = arr.toString()
let strArr2 = arr.toLocaleString()
console.log('数组转字符串: ', strArr1)
console.log('数组转字符串: ', strArr2)
// 数组转字符串: 1,2,3,4,5,5// 数组转字符串: 1,2,3,4,5,5
valueOf()
返回 Array 对象的原始值。
let arr = [1, 2, 3, 4, 5, 5]
let res = arr.valueOf()
console.log('返回 arr 对象的原始值: ', res)
// 返回 arr 对象的原始值: [ 1, 2, 3, 4, 5, 5 ]
你可能没用过的api
copyWithin()
该方法可以将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。该方法可以传入3个参数。注意: IE 11 及更早版本不支持 copyWithin() 方法
- target:要覆盖的起始位置
- start:要复制的起始位置
- end: 要复制的结束位置
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let res = arr.copyWithin(5, 0, 3)
console.log('返回值: ', res, "原数组:", arr)
// 返回值: [ 1, 2, 3, 4, 5,1, 2, 3, 9, 10]
// 原数组: [ 1, 2, 3, 4, 5,1, 2, 3, 9, 10]
fill()
fill方法用于将一个固定值替换数组的元素,该方法可以传入三个参数。
- value:要填充的值
- start:要填充的起始位置
- end:要填充的结束位置
开始填充和结束填充的位置要记住包头不包尾
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let res = arr.fill('--', 0, 2)
console.log('返回值: ', res)
console.log('原数组: ', arr)
// 返回值: ['--', '--', 3, 4, 5, 6, 7, 8, 9, 10 ]
// 返回值: ['--', '--', 3, 4, 5, 6, 7, 8, 9, 10 ]
很尴尬,flat,flatMap,flatten这三个方法我百度了一下,有点懵,所以就先不说着三个函数的用法了。
总结
以上数组的api的用法,是我通过百度和实践得出来的,因为篇幅实在太长了,很多api的用法其实没那么简单,有很多的奇技淫巧的,后面如果有机会会单独拎出来。总之,不断复盘,不断复盘,不断复盘。虽然花费的时间很多,但是这些都是基础,基础扎实,学什么都不虚。