开发中,数组的使用场景非常多,平时也涉及到很多数组的api相关操作,很多时候就算用过几次数组的api,在开发中也很容易忘记,还要谷歌一下,所以本篇文章便是对这块内容有一个比较系统性的总结。
创建一个数组
1. 使用Array构造函数
// 创建空数组
const arr1 = new Array() //
// 创建一个数组长度为3的数组
const arr2 = new Array(3) // [,,]
// 创建一个包含三个元素的数组
const arr3 = new Array(1, 2, 3) // [1,2,3]
在使用Array构造函数时,可以省略new操作符
2. 使用数组字面量(最常用的方式)
const arr = [1, 2, 3]
ES6新增用于创建数组的两个静态方法
1. Array.of() 将一组参数转换为数组实例
定义:返回由所有参数值组成的数组,如果没有参数,就返回一个空数组
目的:解决上述使用构造函数因参数个数不同,导致行为有差异的问题
const arr1 = Array.of(3) // [3]
const arr2 = Array.of(1,2,3) // [1,2,3]
2. Array.from 将类数组结构转换为数组实例
定义:用于将两类数据结构转换为数组(不改变原对象,返回新的数组)
两类数据结构:任何可迭代的结构 && 有一个length属性和可索引元素的结构
参数:
- 第一个参数(必需):类数组对象
- 第二个参数(可选):类似数组map方法,对每个元素进行处理,将处理后的值放入返回的数组
- 第三个参数(可选):用于指定映射函数中的this
// 1. 可迭代对象
const arr1 = Array.form('hello'); // ['h', 'e', 'l', 'l', 'o']
const arr2 = Array.from(new Set(1,2)); // [1, 2]
// 2. 有一个length属性和可索引元素的结构
const arrayLikeObject = {
0: 1,
1: 2,
2: 3,
3: 4,
length: 4
}
const arr = Array.from(arrayLikeObject)
function getArgsArray() {
return Array.from(arguments)
}
常用方法
数组原型提供了非常多的方法,这里分三类进行总结,一类是会改变原数组的值,一类是不会改变原数组,以及数组的遍历方法
改变原数组的方法(9个)
ES5: splice() sort() reverse() pop() push() shift() unshift()
ES6: fill() copyWithin()
1. splice() 删除/添加/替换数组元素
语法:arr.splice(index, howmany, item1, ..., itemX)
参数:
- index: 必需。整数,规定添加或删除元素的位置
- howmany: 可选。要删除的元素数量,如果设置为0,则不会删除项目
- item1, ..., itemX:可选。像数组添加的新元素
返回值:始终返回这样一个数组,它包含从数组中被删除的元素(如果没有删除元素,则返回空数组)
删除元素
let arr = [1, 2, 3, 4, 5, 6, 7];
let removedArr = arr.splice(0, 3);
console.log(arr); // [4,5,6,7]
console.log(removredArr) // [1,2,3]
添加元素
let arr = [1, 2, 3, 4, 5, 6, 7];
let item = arr.splice(0, 0, '添加1' ,'添加2');
console.log(item) // [] 没有删除元素,返回空数组
console.log(arr); // ['添加1','添加2', 1, 2, 3, 4, 5, 6, 7]
替换元素
let arr = [1, 2, 3, 4, 5, 6, 7];
let item = arr.splice(1, 1, '替换我')
console.log(item) // [2]
console.log(arr) // [1, '替换我', 3, 4, 5, 6, 7]
2. sort() 数组排序
定义:对数组元素进行排序,并返回这个数组
默认sort()方法美誉传比较函数的话,默认按字母排序,如果元素不是字符串的话,会调用toString()方法将元素转化为字符的Unicode位点,然后再比较字符。
// 字符串排列 看起来很正常
var a = ["Banana", "Orange", "Apple", "Mango"];
a.sort(); // ["Apple","Banana","Mango","Orange"]
// 数字排序的时候 因为转换成Unicode字符串之后,有些数字比较大会排在后面 这显然不是我们想要的
var a = [10, 1, 3, 20,25,8];
console.log(a.sort()) // [1,10,20,25,3,8];
比较函数的两个参数:
sort的比较函数有两个默认参数,要在函数中接收这两个参数,这两个参数是数组中两个要比较的元素,通常我们用a和b接收两个将要比较的元素:
- 若比较函数返回值<0, 那么a将排到b的前面
- 若比较函数返回值=0,那么a和b的相对位置不变
- 若比较函数返回值>0, 那么a将排到b的后面
sort排序常见用法:
- 数组元素为数字的升序、降序
let arr1 = [10, 1, 3, 4, 20, 4, 25, 8]
// 升序
arr1 = arr1.sort((a, b) => a - b);
console.log(arr) // [1, 3, 4, 4, 8, 10, 20, 25]
// 降序
let arr2 = [10, 1, 3, 4, 20, 4, 25, 8]
arr2 = arr2.sort((a, b) => b - a)
console.log(arr2) // [25,20,10,8,4,4,3,1];
- 数组多条件排序
let arr = [{id:10,age:2},{id:5,age:4},{id:6,age:10},{id:9,age:6},{id:2,age:8},{id:10,age:9}];
arr = arr.sort((a,b) => {
// 如果id的值相等,按照age的值降序
if(a.id === b.id){
return b.age - a.age
}else{
// 如果id的值不相等,按照id的值升序
return a.id - b.id
}
})
conosle.log(arr) // [{"id":2,"age":8},{"id":5,"age":4},{"id":6,"age":10},{"id":9,"age":6},{"id":10,"age":9},{"id":10,"age":2}]
- 自定义比较函数
let arr = [{name:'Koro1'},{name:'Koro1'},{name:'OB'},{name:'Koro1'},{name:'OB'},{name:'OB'}];
arr = arr.sort((a,b) => {
if(a.name === 'Koro1'){
// 如果name是'Koro1' 返回-1 ,-1<0 a排在b的前面
return -1
}else{
// 如果不是的话,a排在b的后面
return 1
}
})
console.log(arr) // [{"name":"Koro1"},{"name":"Koro1"},{"name":"Koro1"},{"name":"OB"},{"name":"OB"},{"name":"OB"}]
3. reverse() 反转数组
定义:用于颠倒数组中元素的顺序, 并返回反转的数组
let arr = [1,2,3]
const arr2 = arr.reverse()
console.log(arr2) // [3,2,1]
console.log(arr) // [3,2,1]
4. pop() 删除数组中的最后一个元素
定义:删除一个数组中的最后一个元素,并返回这个元素
let arr = [1,2,3]
const item = arr.pop()
console.log(item) // 3
console.log(arr) // [1,2]
5. push() 向数组的末尾添加元素
定义:向数组末尾添加一个或多个元素,并返回新的长度
let arr = [1,2,3]
let count = arr.push(4,5,6)
console.log(count) // 6
console.log(arr) // [1,2,3,4,5,6]
6. shift() 删除数组中的第一个元素
定义:删除数组的第一个元素,并返回这个元素
let arr = [1,2,3];
const item = arr.shift();
console.log(item) // 1
console.log(arr) // [2, 3]
7. unshift() 在数组开头添加元素
定义:可向数组的开头添加一个或更多元素,返回新的数组长度
let arr = [1,2,3]
let count = arr.unshift('test1', 'test2')
console.log(count) // 4
console.log(a) // ['test1', 'test2', 1, 2, 3]
8. ES6: fill() 填充数组
定义:可以向一个已有的数组中插入全部或部分相同的值。
包含三个参数:第一个参数是要填充数组的值;第二个参数是填充开始的位置(默认值为0);第三个参数是填充结束的位置(不包含结束索引)
let zeros = [0,0,0,0,0]
zeros = zeros.fill(5) // [5,5,5,5,5]
zeros = zeros.fill(0) // [0,0,0,0,0]
zeros = zeros.fill(6,3) // [0,0,0,6,6]
zeros = zeros.fill(0) // [0,0,0,0,0]
zeros = zeros.fill(7,1,3) // [0,7,7,0,0]
9. ES6: copyWithin()
不常用,自行总结
不改变原数组的方法(8个)
1. slice() 浅拷贝数组的元素
定义: 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象,且原数组不会被修改。
语法:arr.slice(begin , end)
参数:
- begin: 可选,索引值,接受负值,开始索引,默认值为0
- end: 可选, 索引值,接受负值,结束索引(不包括)
let a = ['hello', 'world']
let b = a.slice(0, 1)
console.log(b) // ['hello']
console.log(a) // ['hello', 'world']
b[0] = '改变拷贝的数组'
console.log(a, b) // ['hello', 'world'], ['改变拷贝的数组']
新数组是浅拷贝的,元素是简单数据类型,改变之后不会互相干扰; 如果是复杂数据(对象,数组)的话,改变其中一个,另外一个也会改变。
let a = [{name:'doudou'}];
let b = a.slice();
console.log(b,a); // [{"name":"doudou"}] [{"name":"doudou"}]
a[0].name='改变原数组';
console.log(b,a); // [{"name":"改变原数组"}] [{"name":"改变原数组"}]
b[0].name='改变拷贝数组',b[0].koro='改变拷贝数组'; //[{"name":"改变拷贝数组","koro":"改变拷贝数组"}] [{"name":"改变拷贝数组","koro":"改变拷贝数组"}]
总结:slice()是浅拷贝,对于复杂的数据类型浅拷贝,拷贝的只是指向原数组的指针,所以无论改变原数组,还是浅拷贝的数组,都是改变原数组的数据。
2. join() 数组转字符串
定义:接受一个参数,即字符串分隔符,返回包含所有项的字符串
let a = ['hello', 'world'];
let str = a.join(); // "hello,world"
let str2 = a.join('+'); // "hello+world"
let str3 = [{name:'doudou',age:'23'}, 'test'].join(); // "[object Object],test"
join()/toString()方法在数组元素是数组的时候,会将里面的数组也调用join()/toString(), 如果是对象的话,对象会被转为[object Object]字符串。
3. toLocaleString() 数组转字符串
定义: 返回一个表示数组元素的字符串。该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
let a = [{name:'doudou'}, 23, 'abcd', new Date()];
let str = a.toLocaleString(); // [object Object], 23, abcd, 2018/5/28 下午1:52:20
4. toString() 数组转字符串
定义:可把数组转换为由逗号链接起来的字符串。
语法: array.toString()
该方法的效果和join方法一样,都是用于数组转字符串的,但是与join方法相比没有优势,也不能自定义字符串的分隔符,因此不推荐使用。
5. concat() 合并数组
定义:用于合并两个或多个数组,返回一个新数组
语法:
const newArr = oldArr.concat(arr1, arr2, ..., arrX)
let a = [1, 2, 3];
let b = [4, 5, 6];
//连接两个数组
let newVal = a.concat(b); // [1, 2, 3, 4, 5, 6]
// 连接三个数组
let c = [7, 8, 9]
let newVal2 = a.concat(b, c); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 添加元素
let newVal3 = a.concat('添加元素',b, c,'再加一个'); // [1, 2, 3, "添加元素", 4, 5, 6, 7, 8, 9, "再加一个"]
// 合并嵌套数组 会浅拷贝嵌套数组
let d = [1, 2];
let f = [3, [4]];
let newVal4 = d.concat(f); // [1, 2, 3, [4]]
ES6扩展运算符...合并数组
let a = [2, 3, 4, 5]
let b = [ 4, ...a, 4, 4]
console.log(a,b); // [2, 3, 4, 5] [4,2,3,4,5,4,4]
6. indexOf() 从前往后查找数组是否存在某个元素
语法:arr.lastIndexOf(searchElement, index = 0)
参数:
- searchElement: 必需。表示被查找的元素
- index: 可选。表示搜索的起始位置,接受负值。
let a = ['哈哈', 2, 4, 24, NaN]
console.log(a.indexOf('哈')); // -1
console.log(a.indexOf('NaN')); // -1
console.log(a.indexOf('哈哈')); // 0
indexOf()不能识别NaN
7. lastIndexOf() 从后往前查找数组是否存在某个元素
语法:arr.lastIndexOf(searchElement, index)
参数:
- searchElement: 必需。表示被查找的元素
- index: 可选。逆向查找开始位置,默认值为数组的长度 - 1
关于index有三个规则:
- 正值。如果该值大于或等于数组的长度,则整个数组会被查找。
- 负值。将其视为从数组末尾向前的偏移。(比如-2,从数组最后第二个元素开始往前查找)
- 负值。其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。
let a = ['doudou', 4, 'dami', 1, 2, 'dami', 3 , 4, 5, 'dami']; // 数组长度为10
// let b = a.lastIndexOf('dami', 4); // 从下标4开始往前找 返回下标2
// let b = a.lastIndexOf('dami', 100); // 大于或数组的长度 查找整个数组 返回9
// let b = a.lastIndexOf('dami', -11); // -1 数组不会被查找
let b = a.lastIndexOf('dami', -9); // 从第二个元素4往前查找,没有找到 返回-1
8. ES7:includes() 查找数组是否包含某个元素 返回布尔值
语法:arr.includes(searchElement, index = 0 )
参数:
- searchElement: 必需。表示被查找的元素
- index: 可选。表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false; 负值绝对值超过数组长度,重置从0开始搜索。
includes方法是为了弥补indexOf方法的缺陷而出现的:
- indexOf方法不能识别NaN
- indexOf方法检查是否包含某个值不够与异化,需要判断是否不等于-1,表达不够直观
let arr = ['doudou', 'dami', NaN, 'maomao']
const flag = arr.includes(NaN) / /true
const flag = arr.includes('doudou', 100) // false 超过数组长度,不搜索
const flag = arr.includes('dami', -3) // true 从倒数第三个元素开始搜索
const flag = arr.includes('doudou', -100) // true 负值绝对值超过数组长度,搜索整个数组
数组的遍历方法(12个)
js中遍历数组并不会改变原始数组的方法总共有12个:
ES5: forEach() every() some() filter() map() reduce() reduceRight()
ES6: find() findIndex() keys() values() entries()
1. forEach()
对数组每一项都运行传入的函数,没有返回值。
语法
arr.forEach(function(currentValue, index, arr){
}, thisValue)
参数:
- function: 必须。数组中每个元素需要调用的函数
// 回调函数的参数
1. currentValue(必须), 数组当前元素的值
2. index(可选), 当前元素的索引值
3. arr(可选), 数组对象本身
- thisValue: 可选。当执行回调函数时this绑定对象的值,默认值为undefined
2. every()
对数组每一项都运行传入的函数,如果对每一项函数都返回true, 则这个方法返回true
function isBigEnough(element, index, array) {
return element >= 10; // 判断数组中的所有元素是否都大于10
}
let result = [12, 5, 8, 130, 44].every(isBigEnough); // false
let result = [12, 54, 18, 130, 44].every(isBigEnough); // true
// 接受箭头函数写法
[12, 5, 8, 130, 44].every(x => x >= 10); // false
[12, 54, 18, 130, 44].every(x => x >= 10); // true
3. some()
对数组每一项都运行传入的函数,如果有一项函数返回true, 则这个方法返回true
function isBigEnough(element, index, array) {
return (element >= 10); //数组中是否有一个元素大于 10
}
let result = [2, 5, 8, 1, 4].some(isBigEnough); // false
let result = [12, 5, 8, 1, 4].some(isBigEnough); // true
4. filter()
对数组每一项都运行传入的函数,函数返回true的项会组成数组之后返回。(过滤原始数组,返回新数组)
let a = [32, 33, 16, 40];
let result = a.filter(function (value, index, array) {
return value >= 18;
});
console.log(result);// [32,33,40]
console.log(a); // [32, 33, 16, 40]
5. map()
对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
let a = [1,2,3,4]
let res = a.map(function(currentValue, index, arr) {
return currentValue * 2
})
console.log(res) // [1,4,6,8]
console.log(a) // [1,2,3,4]
6. reduce()
定义:对数组中的每个元素执行一个自定义的累积器,将其结果汇总为单个返回值
语法:arr.reduce((total, value, index, array) => {}, initValue)
参数:
- callback: 回调函数(必需)
- initValue: 初始值(可选)
回调函数的参数:
- total: 累积器完成计算的返回值(必需)
- value: 当前元素(必需)
- index: 当前元素的索引(可选)
- array: 当前元素所属的数组对象(可选)
过程:
- 以total作为累计结果的初始值,不设置total则以数组第一个元素为初始值
- 开始遍历,使用累积器处理value,将value的映射结果累计到total上,结束此循环,返回total
- 进入下一次循环,重复上述操作,直至数组最后一个元素
- 结束遍历,返回最终的total
reduce的精华所在是将累计器逐个作用于数组成员上,把上一次输出的值作为下一次输入的值。下面举个简单的栗子,看看reduce的计算结果
const arr = [3, 5, 1, 4, 2];
const a = arr.reduce((t, v) => t + v);
// 等同于
const b = arr.reduce((t, v) => t + v, 0);
代码不太明白没关系, 可参考这篇文章的动图
reduce实质上是一个累计器函数,通过用户自定义的累计器对数组成员进行自定义累计,得出一个由累计器生成的值。
对空数组调用reduce()和reduceRight()是不会执行其回调函数的,可认为reduce()对空数组无效
高级用法:
- 累加累乘
function getSum(...values) {
return values.reduce((total, value) => total + value, 0);
}
function getMul(...values) {
return values.reduce((total, value) => total * value, 1);
}
const res1 = getSum(1, 2, 3, 4)
console.log(res1)
const res2 = getMul(1, 2, 3, 4)
console.log(res2)
- 权重求和
const scores = [
{ score: 90, subject: "chinese", weight: 0.5 },
{ score: 95, subject: "math", weight: 0.3 },
{ score: 85, subject: "english", weight: 0.2 }
];
const res = scores.reduce((total, value) => total + value.score * value.weight, 0)
console.log(res)
- 代替map和filter
const arr = [0, 1, 2, 3];
// 代替map:[0, 2, 4, 6]
const a = arr.map(v => v * 2);
const b = arr.reduce((arr, v) => {
arr.push(v * 2);
return arr;
}, [])
// b的简洁写法
const b1 = arr.reduce((arr, v) => [...arr, v * 2], [])
console.log(b, b1) // [ 0, 2, 4, 6 ] [ 0, 2, 4, 6 ]
// 代替filter:[2, 3]
const c = arr.filter(v => v > 1);
const d = arr.reduce((arr, value) => {
if(value > 1) {
arr.push(value)
}
return arr;
}, [])
// d的简洁写法
const d1 = arr.reduce((t, v) => v > 1 ? [...t, v] : t, []);
console.log(d, d1) // [ 2, 3 ] [ 2, 3 ]
// 代替map和filter:[4, 6]
const e = arr.map(v => v * 2).filter(v => v > 2);
const f = arr.reduce((t,v) => {
if(v*2 > 2) {
t.push(v*2)
}
return t;
}, [])
// f的简洁写法
const f1 = arr.reduce((t, v) => v * 2 > 2 ? [...t, v * 2] : t, []);
console.group(f, f1) // [ 4, 6 ] [ 4, 6 ]
- 代替some和every
const scores2 = [
{ score: 45, subject: "chinese" },
{ score: 90, subject: "math" },
{ score: 60, subject: "english" }
];
// 代替some: 至少一门合格
const isAtLeastOneQualified = scores2.reduce((t, v) => {
return t || v.score >= 60
}, false)
console.log(isAtLeastOneQualified) // true
// 代替every: 全部合格
const isAllQualified = scores2.reduce((t, v) => {
return t && v.score >= 60
}, true)
console.log(isAllQualified) // false
- 数组过滤
function difference(arr = [], oarr = []) {
const res = arr.reduce((t, v) => {
if(!oarr.includes(v)) {
t.push(v)
}
return t
}, [])
return res
}
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [2, 3, 6]
console.log(difference(arr1, arr2)); // [1, 4, 5]
- 数组扁平化
function flatArr(arr = []) {
const res = arr.reduce((t,v) => {
return t.concat(Array.isArray(v) ? flatArr(v) : v)
}, [])
return res
}
const arr3 = [0, 1, [2, 3], [4, 5, [6, 7]], [8, [9, 10, [11, 12]]]];
console.log(flatArr(arr3)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
- 数组去重
unction uniq(arr = []) {
return arr.reduce((t, v) => {
if(!t.includes(v)){
t.push(v)
}
return t
}, [])
}
const testArr = [2, 1, 0, 3, 2, 1, 2]
console.log(uniq(testArr)) // [2, 1, 0, 3]
- 数组成员统计数量
function count(arr = []) {
return arr.reduce((t, v) => {
if(!t[v]) {
t[v] = 1
} else {
t[v] = t[v] + 1
}
return t
}, {})
}
const countArr = [0, 1, 1, 2, 2, 2];
console.log(count(countArr)) // { '0': 1, '1': 2, '2': 3 }
- 数字千分化
function ThousandNum(num = 0) {
const str = (+num).toString().split(".");
const int = nums => nums.split("").reduce((t, v, i) => t + (i % 3 ? v : `${v},`), "").replace(/^,|,$/g, "");
const dec = nums => nums.split("").reduce((t, v, i) => t + ((i + 1) % 3 ? v : `${v},`), "").replace(/^,|,$/g, "");
return str.length > 1 ? `${int(str[0])}.${dec(str[1])}` : int(str[0]);
}
ThousandNum(1234); // "1,234"
ThousandNum(1234.00); // "1,234"
ThousandNum(0.1234); // "0.123,4"
ThousandNum(1234.5678); // "1,234.567,8"
- 数组转对象
const people = [
{ area: "GZ", name: "YZW", age: 27 },
{ area: "SZ", name: "TYJ", age: 25 }
];
const map = people.reduce((t, v) => {
const { name, ...res} = v
t[name] = res
return t
}, {})
console.log(map) // // { YZW: {…}, TYJ: {…} }
7. reduceRight()
这个方法除了与reduce执行方向相反外,其他完全与其一致,参考上述 reduce 方法介绍。
// 代替reverse
function reverseArr(arr = []) {
return arr.reduceRight((t, v) => {
t.push(v);
return t;
} , [])
}
const res = reverseArr([1, 2, 3, 4, 5])
console.log(res) // [5,4,3,2,1]
8. ES6: find() & findeIndex()
find()定义:用于找出第一个符合条件的数组元素,并返回该成员,如果没有符合条件的元素,则返回undefined。
findIndex()定义:返回第一个符合条件的数组元素的位置,如果所有元素都不符合条件,则返回-1。
语法:
let item = arr.find(function(currentValue, index, arr), thisArg)
let index = arr.findIndex(function(currentValue, index, arr), thisArg)
这两个方法都可以识别NaN,弥补了indexOf的不足.
// find
let a = [1, 4, -5, 10].find((n) => n < 0); // 返回元素-5
let b = [1, 4, -5, 10,NaN].find((n) => Object.is(NaN, n)); // 返回元素NaN
// findIndex
let a = [1, 4, -5, 10].findIndex((n) => n < 0); // 返回索引2
let b = [1, 4, -5, 10,NaN].findIndex((n) => Object.is(NaN, n)); // 返回索引4
9. ES6 keys() & values() & entries()
定义:三个方法都返回一个新的 Array Iterator对象,对象根据方法不同包含不同的值。
语法:
arr.keys()
arr.values()
arr.entries()