你真的了解JavaScript中的数组操作吗?
其实这篇文章拖延了很久很久,说实话工作也没那么忙,但是总觉得冥冥之中总有一种不可抗力总是拖延着你,不让你去写博客,曾经千万次的告诉自己:我一定要坚持每周至少写一篇!但是事与愿违,我知道,这一切都是借口…算了,回归正题!
大家看到这个标题,可能会很自信的说,JavaScript数组操作这么简单…真的那么简单吗?对于很多资深前端开发人员,本文似乎有些小儿科!当然,还请各位大佬多多指教!
为什么要特别写一篇关于数组操作的文章,因为数组是在我们实际项目最常用的一种变量类型,我们常常要处理很多后台返回的数据,数组操作不当或者没有特别“优雅”的处理数组数据,那会使你的项目变得晦涩难懂,冗余复杂,这一点我相信大家都有深深的体会
好啦,废话不多说!接下来我会详细的介绍数组操作方法:
(一)常规操作
常规操作就是最基础的数组的定义及方法
1.定义数组
数组的定义有很多种方式:
let arr = [] //(最常用)
let arr = new Array() // 使用 Array 构造函数创建
// 当然我们也可以这么创建
let arr = new Array(5) // 创建一个长度为 5 的数组
console.log(arr) // [empty x 5]
// 定义数组的方式当然还有很多种方式,但基本上就这两种方式:一种是使用字面量方式,一种是使用Array构造函数的方式创建!
2.push()方法
说明:向数组的末尾添加一个或者多个元素,改变原数组,并返回新数组的长度。
let names = ['zhangsan', 'lisi']
let namesLen = names.push('wangwu')
console.log(names) // ['zhangsan', 'lisi', 'wangwu']
console.log(namesLen) // 3
// 当然也可以传多个数组
names.push('zhaoliu', 'houqi')
console.log(names) // ['zhangsan', 'lisi', 'wangwu', 'zhaoliu', 'houqi']
3.unshift()方法
说明:向多个数组的开头添加一个或多个数组,改变原数组,并返回新数组的长度
let names = ['zhangsan', 'lisi']
let namesLen = names.unshift('wangwu')
console.log(names) // ['wangwu', 'zhangsan', 'lisi' ]
console.log(namesLen) // 3
// 当然也可以传多个数组
names.push('zhaoliu', 'houqi')
console.log(names) // ['zhaoliu', 'houqi', 'wangwu', 'zhangsan', 'lisi']
4.pop()方法
说明:删除数组的最后一个元素,改变原数组,并返回该元素的值
let names = ['zhangsan', 'lisi', 'wangwu']
let unitNames = names.pop()
console.log(names) // ['zhangsan', 'lisi']
console.log(unitNames) // wangwu
5.shift()方法
说明:删除数组的第一个元素,改变原数组,并返回该数组的值
let names = ['zhangsan', 'lisi', 'wangwu']
let unitNames = names.shift()
console.log(names) // ['lisi', 'wangwu']
console.log(unitNames) // zhangsan
6.solice()方法
此方法是数组基本操作中非常强大的,它有删除、插入和替换的功能,接下来一一介绍它。
- 删除
可以删除任意的项;
接收两个参数,第一个参数为起始项,第二个参数是要删除的数量,并返回已删除的项(数组类型) 例如:
let names = ['zhangsan', 'lisi', 'wangwu']
let deleteArr = names.splice(0, 1)
console.log(names) // ['lisi', 'wangwu']
console.log(deleteArr) // ['zhangsan']
- 插入
可以向指定位置插入任意数量的项。
接收n个参数,第一个参数为起始项,第二个为0,后面的参数就是为要插入的数据,例如:
let names = ['zhangsan', 'lisi', 'wangwu']
names.splice(2, 0, 'lisi1', 'lisi2', 'lisi3')
console.log(names) // ['zhangsan', 'lisi', 'lisi1', 'lisi2', 'lisi3', 'wangwu']
- 替换
可以向指定位置插入任意数量的项,并且同时删除任意数量的项。
接收n个参数,第一个参数为起始项,第二个参数为要替换的数量,后面的参数就是所替换的数据,例如:
let names = ['zhangsan', 'lisi', 'wangwu']
names.splice(1, 2, 'lisi1', 'lisi2', 'lisi3')
console.log(names) // ['zhangsan', 'lisi1', 'lisi2', 'lisi3']
7.join()方法
说明:把数组变成一个字符串,分隔符为传入的字符
let names = ['zhangsan', 'lisi', 'wangwu']
let arrToStr = names.join(',')
console.log(arrToStr) // 'zhangsan,lisi,wangwu'
9.sort()方法
说明:对数组进行排序
sort()方法在使用时,它会调用每个数组项的 toString() 方法,然后比较得到的字符串,以确定如何排序。即使每一项都是数字,sort()比较的也是数值。
let values = [1, 2, 5, 10, 15]
values.sort()
console.log(values) // [1, 10, 15, 2, 5]
可以看到,sort()方法并没有按照从大到小的顺序去排列,它先是把数字转换成字符串再去比较的。‘10’,和 ‘5’ 比较, 自然 ’5‘大。然而sort()在实际项目中往往用的是另一种方法:
let values = [1, 2, 5, 15, 10]
const compare = function(val1, val2) {
if(val1 < val2) {
return -1
} else if(val1 > val2) {
return 1
} else {
return 0
}
}
values.sort(compare)
console.log(values) // [1, 2, 5, 10, 15]
当然也可以将 compare() 函数简化一下:
const compare = function(val1, val2) {
return val2 - val1 // 从小到大的顺序
// return val1 - val2 // 从大小的顺序
}
10.slice()方法
说明: 分割数组,可以接收一个或者两个参数;一个参数时,slice() 方法返回从该数组指定位置开始到当前数组末尾的所有的项。两个参数时,会返回数组从起始位置和结束位置之间的项。该方法不会影响原数组。
let names = ['zhangsan', 'lisi', 'wangwu', 'zhaoliu', 'houqi']
let names2 = names.slice(1)
let names3 = names.slice(1, 3)
console.log(names2) // ['lisi', 'wangwu', 'zhaoliu', 'houqi']
console.log(names3) // ['lisi', 'wangwu']
11.concat()方法
12.indexOf()方法
说明:返回获取项在数组中第一次出现的索引
let names = ['zhangsan', 'lisi', 'wangwu', 'lisi']
let index = namse.indexOf('lisi')
console.log(index) // 1
// 这里打印的是1,而不是3,说明 indexOf() 方法,返回传入项的第一次出现的索引
13.lastIndexOf()方法
说明:返回获取项在数组中最后一次出现的索引
let names = ['zhangsan', 'lisi', 'wangwu', 'lisi']
let index = namse.lastIndexOf('lisi')
console.log(index) // 3
// 这个和 indexof() 相对
(二)数组遍历
接下来是一些常用的数组遍历方法
1.forEach()
说明: 遍历数组中的每一项,对每一项运行给定函数,该方法没有返回值
2.some()
说明:遍历数组中的每一项,对每一项运行给定函数,如果该函数任意一项返回true,则返回true
let values = [1, 2, 6, 35, 46]
let flag = values.some((item, index, array) => {
return item > 10
})
console.log(flag) // true
3.every()
let values = [1, 2, 6, 35, 46]
let flag = values.every((item, index, array) => {
return item > 10
})
console.log(flag) // false
4.find()
说明:找到符合条件的数组中的项,并立即结束循环,返回该项
let values = [1, 2, 6, 35, 46]
let newValue = values.find((item, index, array) => {
return item > 10
})
console.log(newValue) // 35
5.findIndex()
说明:找到符合条件的数组中的项,并立即结束循环,返回该项的索引
let values = [1, 2, 6, 35, 46]
let newValue = values.findIndex((item, index, array) => {
return item > 10
})
console.log(newValue) // 3
6.filter()
说明:遍历数组中的每一项,对每一项运行给定函数,返回该函数为 true 的数组。
let values = [1, 2, 6, 35, 46]
let newValues = values.filter((item, index, array) => {
return item > 10
})
console.log(newValue) // [35, 46]
7.map()
说明:遍历数组中的每一项,对每一项运行给定函数,返回每次函数调用结果后组成的数组。
let values = [1, 2, 6, 35, 46]
let newValues = values.map((item, index, array) => {
return item + 1
})
console.log(newValue) // [2, 3, 7, 36, 47]
8.reduce()
说明:对数组中的每一个元素执行一个自定义累加器,将其结果汇总为单个返回值。
这样解释可能有些不好理解,接下来有例子,一看就明白了。reduce() 是数组操作中最强大的方法,也是最好用的方法!(非常重要!!!),为什么这么说呢,因为它可以实现上述介绍的所有方法!
它可以接收两个参数,一个是在每一项上都会调用的函数,一个是称为累加(或归并)基础的初值!第二个参数是可选的。
第一个参数作为函数,它可以接收四个参数,分别是:前一个值、当前值、当前项的索引和数组本身。一如既往,我们直接上例子:
将数组累加
let values = [1, 2, 3, 4, 5]
let sum = values.reduce((pre, cur, index, array) => {
return pre + cur
})
console.log(sum) // 15
reduceRight() 方法和 reduce() 方法一样,只不过一个是正序,一个是倒序!
(三)实际案例
接下来就用一些实际的例子,让大家看下同一个需求,不同的写法,择优而用!
- 取一个长度为 100 的数组,并保证每一项为 0~1000 的随机整数
// 方法一 Array.from() 方法
let values1 = Array.from(Array(100), (item,index) => parseInt(Math.random()*1000))
// 方法二 用 fill 方法
let values2 = new Array(100).fill(0).map(() => parseInt(Math.random()*1000)))
// 方法三
let values3 = [...Array(100).map(() => parseInt(Math.random()*1000))]
注意:new Array() 会生成一个有100空位的数组,这个数组不能被 map(), forEach(), filter(), every(), some() 遍历的,因为空位会被跳过(for of 不会被跳过,可以遍历)。
[…Array(100)] 可以给空位设置默认值 undefined ,从而可以被上述方法遍历。
我再也不想看到下面这样的代码:
let values = []
for(let i = 0;i < 100;i++){
let val = parseInt(Math.random()*1000)
values.push(val)
}
- 数组合并
let values1 = Array.from(Array(100), (item,index) => parseInt(Math.random()*1000))
let values2 = new Array(100).fill(0).map(() => parseInt(Math.random()*1000)))
let values3 = [...Array(100).map(() => parseInt(Math.random()*1000))]
// 方法一
values1.concat(values2, values3)
// 方法二
let values = [...values1, ...values2, ...values3]
- 数组去重
let values = [1, 1, 2, 3, 3, 4, 5, 5]
let newValues = [...new Set(values)];
console.log(newValues) // [1,2,3,4,5]
new Set() 方法接收一个数组参数,并生成一个 set 结构的数据类型,set 数据类型的元素不会重复,且是 Array Interator,利用这一特性可以去重。
当然,如果你愿意用下面的方法的话,那就随心就好:
let values = [1, 1, 2, 3, 3, 4, 5, 5]
let newValues = []
for(let i = 0;i < values.length;i++) {
if(newValues.indexOf(values[i]) < 0) {
newValues.push(values[i])
}
}
console.log(newValues) // [1,2,3,4,5]
- 数组取交集
let values1 = [0,1,2,3,4,5,6]
let values2 = [3,4,5,6,7]
let duplicValues = [...new Set(values1)].filter(item => values2.includes(item));
console.log(duplicValues) // [3,4,5,6]
- 数组取差集
let values1 = [0,1,2,3,4,5,6]
let values2 = [3,4,5,6,7]
let duplicValues = [...new Set([...values1,...values2])].filter(item => !values1.includes(item) || !values2.includes(item));
console.log(duplicValues) // [0,1,2,7]
当然,看到这里的小伙伴会说:“实际项目中哪有数组都是数字的?基本都是对象数组!”是的,这个确实是事实,那接下来我就举一些对象数组的例子
- 去重
我们以id
的维度去重
let persons = [
{id:1,name:'Jack',age:18,sex:'男'},
{id:2,name:'Kevin',age:21,sex:'男'},
{id:3,name:'Jessica',age:23,sex:'女'},
{id:4,name:'Sarah',age:19,sex:'女'},
{id:2,name:'Kevin',age:21,sex:'男'},
]
let unique = {};
let arr = persons.reduce((pre, cur) => {
unique[cur.id] ? '' : unique[cur.id] = true && pre.push(cur)
return pre
},[])
上面用的是万能而又强大的 reduce() 方法,当然你也可以这么写,结果是一样的:
let persons = [
{id:1,name:'Jack',age:18,sex:'男'},
{id:2,name:'Kevin',age:21,sex:'男'},
{id:3,name:'Jessica',age:23,sex:'女'},
{id:4,name:'Sarah',age:19,sex:'女'},
{id:2,name:'Kevin',age:21,sex:'男'},
]
const unique = function(array, key) {
let resultArr = []
let paramsArr = array.map(item => item[key])
let newArr = [...new Set(paramArr)]
newArr.forEach(item1 => {
for(let j = 0;j < array.length;j++) {
if(item1 === array[j][key]) {
resultArr.push(array[j])
break;
}
}
})
return resultArr;
}
console.log(unique(persons, 'id'))
文章到这里就结束了,当然也有很多不足或者很多操作有更简洁的方法,希望大佬们批评指正,鉴于我把 reduce() 方法吹嘘的神乎其神,后续我会专门整理一个 reduce() 方法的介绍和它强大的应用
谢谢!