数组
# 数组
参考资料:js 数组详细操作方法及解析合集 (opens new window)、JS数组奇巧淫技 (opens new window)
[TOC]
# 一、常用操作
# 1.1 创建数组
# 1.1.1 new Array()
- 一个参数即为数组长度,多个参数即为参数值。
let arr1 = new Array(); //[]
let arr2 = new Array(3); //[,,]
let arr3 = new Array(1,2,3);//[1,2,3]
2
3
# 1.1.2 Array.of()
- 返回由所有参数值组成的数组,如果没有参数,就返回一个空数组。
- 解决构造器因参数个数不同,导致的行为有差异的问题。
let arr1 = Array.of(); //[]
let arr2 = Array.of(3); //[3]
let arr3 = Array.of(1,2,3); //[1.2.3]
2
3
# 1.1.3 Array.from()
- 用于将两类对象转为真正的数组(不改变原对象,返回新的数组)。
# 1.1.3.1 类似数组的对象
let obj = {0: 'a', 1: 'b', length: 3};
let arr = Array.from(obj); // ['a','b',undefined]
2
- ES5写法:
[].slice.call(obj); // [1,2,empty]
- 对象的属性名不能转换成索引号时,无效
Array.from({
a: '1',
b: '2',
length:2
}); // [undefined,undefined]
2
3
4
5
- 常见的类似数组对象(必有length属性)
DOM操作返回的NodeList集合
函数内部的arguments对象
# 1.1.3.2 部署了 Iterator接口的数据结构
let arr = Array.from('hello'); // ['h','e','l','l','o']
let arr = Array.from(new Set(['a','b'])); // ['a','b']
2
- 常见的部署了 Iterator 接口的数据结构
NodeList对象
函数内部的arguments对象
string
Set结构
Map结构
Array
TypedArray
- 扩展运算符(...)也可以将部署了 Iterator 接口的数据结构转换为数组,但是不可以将只具有length属性的对象转换为数组。
[...document.query/selectorAll('div')]
# 1.1.3.3 参数
- 第一个参数(必需):要转化为真正数组的对象。
- 第二个参数(可选): 提供map功能。
// 不改变原数组
let s = '123';
Array.from(s, o => o + o); // ['11', '22', '33']
console.log(s); // '123'
2
3
4
- 第三个参数(可选): map函数中this指向的对象。
- 可以用来解耦,将处理的数据和处理方法分离。
let methods = {
addOne: n => n + 1
}
let data = [1, 2, 3];
// 注意不要使用箭头函数,否则在定义就已经指向window
Array.from(data, function(n) {
return this.addOne(n);
} , methods); // [2, 3, 4]
2
3
4
5
6
7
8
# 1.2 不改变原数组
# 1.2.1 flat(n)
- 会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
let arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]
let arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]
let arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2); // [1, 2, 3, 4, 5, 6]
2
3
4
5
6
7
8
- 使用 Infinity 作为深度,展开任意深度的嵌套数组。
arr3.flat(Infinity); // [1, 2, 3, 4, 5, 6]
let arr4 = [1,2,{x:1,y:1},4,[5,6]];
console.log(arr4.flat(Infinity)); //[1,2,{x:1,y:1},4,5,6]
2
3
4
- 移除数组中的空项。
let arr5 = [1, 2, , 4, 5];
arr5.flat(); // [1, 2, 4, 5]
2
# 1.2.2.1 兼容性问题
- 不兼容IE浏览器
// 展开一层数组
arr.reduce((acc, val) => acc.concat(val), []);
// 展开无限层绩效
function flatDeep(arr, d = 1) {
return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
: arr.slice();
};
flatDeep(arr1, Infinity);
2
3
4
5
6
7
8
# 1.2.2 concat()
用于合并两个或多个数组,返回新数组。
let array1 = ['a', 'b', 'c'];
let array2 = ['d', 'e', 'f'];
console.log(array1.concat(array2)); // ["a", "b", "c", "d", "e", "f"]
2
3
4
# 1.2.4 slice()
- 浅拷贝数组,返回拷贝的数组。
- 参数
- begin(可选): 索引数值,接受负值,从该索引处开始提取原数组中的元素,默认值为0。
- end(可选):索引数值(不包括),接受负值,在该索引处前结束提取原数组元素,默认值为数组末尾(包括最后一个元素)。
# 1.2.5 join()
- 把数组中的所有元素通过指定的分隔符进行分隔放入一个字符串,返回生成的字符串。
# 1.2.6 查找
# 1.2.6.1 indexOf()
- 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
// 数组的indexOf使用严格相等===搜索元素
let a=['xiaolin',2,4,24,NaN,'xiaolin']
console.log(a.indexOf('xiao')); // -1
console.log(a.indexOf('xiaolin')); // 0
2
3
4
- indexOf()不能识别NaN。
console.log(a.indexOf('NaN')); // -1
# 1.2.6.2 lastIndexOf()
- 返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1。
a.lastIndexOf('xiaolin'); // 5
# 1.2.6.3 includes()
- 返回一个布尔值。
- 弥补indexOf方法的缺陷而出现的:
- indexOf方法不能识别
NaN
。 - 更加语义化,无需判断返回值是否为-1。
- indexOf方法不能识别
# 1.3 改变原数组
# 1.3.1 fill(fillValue,start=0,end=this.length)
let array1 = [1, 2, 3, 4];
console.log(array1.fill(0, 2, 4)); // [1, 2, 0, 0]
console.log(array1.fill(5, 1)); // [1, 5, 5, 5]
console.log(array1.fill(6)); // [6, 6, 6, 6]
2
3
4
5
6
7
# 1.3.2 push()和unshift()
- 返回数组的新长度。
# 1.3.3 pop()和shift()
- 返回被删除的元素,空数组则返回undefined
- 不接受传参,传了也没用。
# 1.3.4 splice()
- 向/从数组中添加/删除项目,返回被删除的项目,添加时返回[]。
- 参数:
- index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
- deleteNum:可选。要删除的项目数量,默认到结尾。如果设置为 0,则不会删除项目。
- addItem1, ..., addItemX: 可选。向数组添加的新项目。
let arr = [1,2,3,4];
let res = arr.splice(1); // [2,3,4]
2
# 1.3.5 sort()
对数组元素默认默认按照Unicode编码,从小到大进行排序。
字符串排列时,直接sort()就是升序。
let a = ["Banana", "Orange", "Apple", "Mango"];
a.sort(); // ["Apple","Banana","Mango","Orange"]
2
- 数字排序的时候,会先转换成Unicode字符串排列。
let a = [10, 1, 3, 20,25,8];
a.sort(); // [1,10,20,25,3,8];
2
- sort的比较函数有两个默认参数,通常我们用 a 和 b 接收两个将要比较的元素:
- 若比较函数返回值<0,那么a将排到b的前面;
- 若比较函数返回值=0,那么a 和 b 相对位置不变;
- 若比较函数返回值>0,那么b 排在a 将的前面。
let array = [10, 1, 3, 4,20,4,25,8];
// 升序
array.sort(function(a,b){
return a-b;
}); // [1,3,4,4,8,10,20,25];
// 降序
array.sort(function(a,b){
return b-a;
}); // [25,20,10,8,4,4,3,1];
2
3
4
5
6
7
8
9
10
11
# 1.3.6 reverse()
- 颠倒数组中元素的顺序,返回颠倒后的数组。
# 1.3.7 copyWithin(targetIndex, start = 0, end = this.length)
- 指定位置的成员复制到其他位置,并返回数组。
- 从targetIndex位置开始替换数据,从start位置开始读取数据,到end位置前停止读取数据。
// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1) // [4, 2, 3, 4, 5]
2
# 1.3.8 性能问题
shift和unshift是对于数组的首元素进行操作的,由于数组是个引用类型的数据类型,它在变量赋予的时候其实是赋予了一个指针,也就是说我们首元素出列了或者有元素入列了,我们变量名所挂钩的指针是不会变的,所以说每次对首元素操作后面的元素都会受到影响,都会有一步操作。
而 pop 和 push 这样的操作,每次只会对尾元素进行操作,不会对整个数组的元素有影响。
在 Chrome 中,push方法的实现是用的汇编,而pop、unshift以及shift则是是用的 C++,这里就单独拎出unshift的来看看:
- 先判断容量是否足够。
- 如果不够,将容量扩展,然后把老元素移到新的内存空间偏移为unshift元素个数的位置,也就是说要腾出起始的空间放unshfit传进来的元素。
- 如果空间足够了,则直接执行memmove移动内存空间,最后再把unshif传进来的参数copy到开始的位置。
- 最后更新 array length。
作者:uni-5 链接:https://leetcode-cn.com/problems/implement-stack-using-queues/solution/javascriptjie-fa-yi-ji-dui-javascriptbu-fen-apixin/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
用reverse()+push()代替unshift()
var arr1 = Array(10000).fill(1)
var s = +new Date()
for(let i = 0; i < 10000; i++) {
arr1.unshift(i)
}
console.log(+new Date - s) // 23
var arr2 = Array(10000).fill(1)
var s = +new Date()
arr2.reverse()
for(let i = 0; i < 10000; i++) {
arr2.push(i)
}
arr2.reverse()
console.log(+new Date - s) // 6
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1.4 遍历数组
不改变原数组。
ES5: forEach、every 、some、 filter、map、reduce、reduceRight。
ES6:find、findIndex、keys、values、entries。
遍历规则
- 对于空数组是不会执行回调函数的。
- 对于已在迭代过程中删除的元素,或者空元素会跳过回调函数。
- 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
- 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。
注意事项
- 尽量不要在遍历的时候修改数组的长度(删除/添加)。
- 尽量不要在遍历的时候,修改后面要遍历的值。
# 1.4.1 ES5遍历
# 1.4.1.1 forEach()
- 按升序为数组中含有效值的每一项执行一次回调函数。
array.forEach(function(currentValue, index, arr), ?thisValue)
let array = [1, 2, 3];
array.forEach((n, index) => n - index); // undefined
let arr = [];
array.forEach((n, index) => arr[index] = n - index);
console.log(arr); // [1, 1, 1]
2
3
4
5
- 注意事项
- 无法中途退出循环,只能用
return
退出本次回调,进行下一次回调。 - 总是返回 undefined值,即使你return了一个值。
- 无法中途退出循环,只能用
# 1.4.1.2 every()
- 检测数组中所有元素是否都符合判断条件。
array.every(function(currentValue, index, arr), ?thisValue)
。- 遍历规则
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测。
- 如果所有元素都满足条件,则返回 true。
let arr = [1, 2, 3, 4];
arr.every(n => {
console.log(n);
return n<3;
})
// 1 2 3 false
2
3
4
5
6
# 1.4.1.3 some()
- 检测数组中是否有满足判断条件的元素。
array.some(function(currentValue, index, arr), ?thisValue)
。- 遍历规则
- 如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测。
- 如果没有满足条件的元素,则返回false。
let arr = [1, 2, 3, 4];
arr.some(n => {
console.log(n);
return n<3;
})
// 1 true
2
3
4
5
6
# 1.4.1.4 filter()
- 返回一个新数组, 其包含通过所提供函数实现的测试的所有元素。
array.filter(function(currentValue, index, arr), ?thisValue)
。
let arr = [1, 2, 3, 4];
arr.filter(n => {
return n<3;
}); // [1, 2]
2
3
4
# 1.4.1.5 map()
- 对数组中的每个元素进行处理,返回新的数组。
array.map(function(currentValue, index, arr), ?thisValue)
。
let arr = [1, 2, 3, 4];
arr.map(n => n * n); // [1, 4, 9, 16]
2
# 1.4.1.6 reduce()
- 对数组中的每个元素执行一个提供的reducer函数,将其结果汇总为单个返回值。
array.reduce(function(accumulator, currentValue, currentIndex, arr), initialValue)
- 这里的array不能是空数组
let arr = [1, 2, 3, 4];
let reducer = (accumulator, currentValue) => accumulator + currentValue;
console.log(arr.reduce(reducer)); // 10
console.log(arr.reduce(reducer, 5)); // 15
2
3
4
- 获取数组前后值
let arr = [1,2,3,4]
let reducer = (a,b) => {
console.log(a,b)
return b
}
a.reduce(reducer)
// 1 2 // 2 3 // 3 4
a.reduce(reducer,99)
// 99 1 // 1 2 // 2 3 // 3 4
2
3
4
5
6
7
8
9
# 1.4.1.7 reduceRight()
- 除了与reduce执行方向相反外,其余完全一致。
# 1.4.2 ES6遍历
# 1.4.2.1 find()和findIndex()
find():用于找出第一个符合条件的数组成员,并返回该成员,如果没有符合条件的成员,则返回undefined。
array.find(function(currentValue, index, arr), ?thisArg)
findIndex():返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
arr.findIndex(function(currentValue, index, arr), ?thisArg)
两个方法都可以识别
NaN
,弥补了indexOf
的不足。
let arr = [1, 2, NaN, NaN];
arr.find(n => Object.is(NaN, n)); // NaN
arr.findIndex(n => Object.is(NaN, n)); // 2
2
3
# 1.4.2.2 keys()、values()和entries()
- 遍历键名、遍历键值、遍历键值对,返回遍历器对象。
- 无参数。
- 返回结果可用for...of循环遍历。
let arr = [1, 2, 3, 4];
arr.keys(); // Array Iterator {}
JSON.stringify(arr.key()); // '{}'
for(let key of arr.keys()){
console.log(key); // 0 1 2 3
}
for(let value of arr.values()){
console.log(value); // 1 2 3 4
}
for(let keyValue of arr.entries()){
console.log(keyValue);
}
// [0, 1] [1, 2] [2, 3] [3, 4]
// 解构赋值
for(let [index, value] of arr.entries()){
console.log(index, value);
}
// 0 1
// 1 2
// 2 3
// 3 4
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1.5 检测数组
# 1.5.1 Array.isArray()
Array.isArray([]); // true
- Array.prototype 也是一个数组。
Array.isArray(Array.prototype); // true
- 当检测Array实例时,
Array.isArray
优于instanceof,因为Array.isArray能检测iframes
。