文章目录
数组的扩展
扩展运算符
定义
扩展运算符(spread)是三个点(...
)。将一个数组转为用逗号分隔的参数序列
console.log(...[1,2,3]); // 1 2 3
console.log(1, ...[2,3,4],5); // 1 2 3 4 5
应用
扩展运算符主要用于函数调用。
function push(array, ...item){
array.push(...item);
}
function add(x, y){
return x + y;
}
const numbers = [4, 28];
add(...numbers); // 32
- 扩展运算符和正常的函数参数可以结合使用,非常灵活。
function f(a,b,c,d,e){}
const args = [1,2];
f(-1, ...args,2,...[3]);
- 扩展运算符后面还可以放置表达式
const demo = [...(3 > 0 ? ['a'] : [], 'b')];
- 如果扩展运算符后面是一个空数组,则不产生任何效果
- 只有函数调用时,扩展运算符才可以放在圆括号里,否则会报错
(...[1,2]); // 报错
console.log((...[1,2])); // 报错
console.log(...[1,2]); // 1 2
- 替代函数的
apply
方法
由于扩展运算符可以展开数组,所以不再需要apply
方法,将数组转为函数的参数了。
// ES5
function f(x,y,z){
// ...
}
var args = [0,1,2];
f.apply(null,args);
// ES6
function f(x,y,z){
// ...
}
let args = [0,1,2];
f(...args);
- 复制数组
数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。
const a1 = [1,2]
const a2 = a1;
a2[0] = 2;
console.log(a1); // [2,2]
在***ES5中***
const a1 = [1,2];
const a2 = a1.concat();
a2[0] = 2;
console.log(a1); // [1,2]
在***ES6中使用扩展运算符***
const a1 = [1,2];
// 第一种写法
const a2 = [...a1];
// 第二种写法
const [...a2] = a1;
- 合并数组
const arr1 = ['a', 'c'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5中
arr1.concat(arr2,arr3);
// ES6中
[...arr1, ...arr2, ...arr3];
需要注意的是,上面的这两种都是浅拷贝,如果修改了原来数组的成员,回同步反映到新数组的。
- 与解构赋值结合
// ES5
a = list[0], rest = list.slice(1);
// ES6
[a, ...rest] = list;
const [first, ...rest] = [1,2,3,4,5];
console.log(first); // 1
console.log(rest); // [2,3,4,5]
注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
- 字符串
扩展运算符可以将字符串转成正真的数组
console.log([...'hello']); // ["h", "e", "l", "l", "o"]
扩展运算符可以识别正确的双字符的Unicode字符。
'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
凡是设计到操作符四个字节的Unicode字符的函数,会好使用扩展运算符改写
- 实现
Iterator
接口的对象
任何定义了遍历器(Iterator
)接口的对象,都可以用扩展运算符转为真正的数组。
let nodelist = document.quertSelectorAll('div');
let array = [...nodelist];
方法quertSelectorAll
返回一个NodeList
对象,由于NodeList
对象实现了Iterator
所以可以使用扩展运算符将其转化为一个真正的数组。
对于没有Iterator
接口的对象,使用扩展运算符将会报错。
Array.from()
方法Array.from()
用于将类似数组的对象(array-like object)和可遍历(Iterable)对象(包括ES6新增的数据结构Set和Map)转为真正的数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
}
// ES5
var arr1 = [].slice.call(arrayLike);
// ES6
var arr2 = Array.from(arrayLike);
只要部署了Iterator接口的数据结构,Array.from
都能将其转化为数组
Array.from('hello'); // ['h','e','l','l','o']
let nameSet = new Set(['a','b']);
Array.from(nameSet); // ['a','b']
如果参数是一个真正的数组,Array.from
会返回一个一模一样的数组
Array.from([1,2,3]); // [1,2,3]
Array.from
还可以接受第二个参数,类似于数组的map
方法,用来对每一个元素进行处理,将处理后的值放入返回的数组中。
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.of()
方法Array.of()
用于将一组值转换为数组
Array.of(1,2,3); // [1,2,3]
Array.of(3).length // 1
方法的主要目的
方法Array.of()
主要是弥补数组构造函数Array()
的不足。因为参数个数的不同,会导致Array()
的行为有差异。
Array(); // []
Array(3); // [, , ,]
Array(1,23,4); // [1,23,4]
方法Array()
在没有参数、一个参数、两个参数、三个参数时返回的结果都不一样。只有当参数个数不少于2个时,Array()
才会返回由参数组成的新数组。参数只有一个的时候,实际上是在指定数组的长度。
数组实例的copyWithin()
方法copyWithin()
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。需要注意:使用这个方法会改变当前数组。
Array.prototype.copyWithin(target, start = 0,end = this.length)
这个方法接受三个参数:
- target(必需):开始替换数据的位置,如果是负数,表示倒数。
- start(可选):开始读取数据的位置,默认是0,如果是负数表示从末尾开始计算。
- end(可选):停止读取数据的位置,默认是数组的长度,如果是负数,就从末尾开始计算。
数组实例的find()和findIndex()
方法find()
用于找出第一个符合条件的数组元素,参数是一个回调函数,如果没有找到返回undefined
.回调函数中接受三个参数:当前值、当前的位置、原数组。
[1,2.3,23,13].find((value,index,arr)=>{
return value > 9;
})
数组实例的fill()
方法fill()
使用给定值填充一个数组
['a','e','d'].fill(7); // [7,7,7]
new Array(3).fill(2); // [2,2,2]
方法fill()
用于空数组的初始化非常方便。数组中已经有的元素会被全部抹去。
方法fill()
还可以接受第二个第三个参数,用于填充的起始位置和结束位置。
注意:如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象
数组实例的entries()、keys()和values()
方法entries()
、keys()
和values()
用于遍历数组。可以使用for...of
循环进行遍历,区别是:keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
- 如果不使用
for...of
循环,可以手动调用遍历器对象的next()
方法。
数组实例的includes()
方法includes()
返回一个boolean值,表示某一个数组是否包含给定的值,与字符串的includes
方法类似。ES2016引入该方法
方法includes()
的第二个参数表示搜索的开始位置,默认是0
,如果是负数,怎么表示倒数的位置。当这个参数的值大于数组长度,就会重置为0
开始。
- Map和Set数据结构有一个
has()
方法,需要注意和includes
区分. - Map结构的
has()
方法用来查找键名的。 - Set结构的
has()
方法用来查找值的。
数组实例的flat()和flatMap()
数组的元素有时还是数组,所以方法flat()
可以将嵌套的数组元素“拉平”,变成一维数组。这个方法返回一个新的数组,对原数据没有影响。
[1,2,3,[4,5],6].flat(); // [1,2,3,4,5,6]
方法flat()
默认只拉平一层,如果需要多层,就可以设置参数。
[1,2,[3,[4,5]],6].flat(2); // [1,2,3,4,5,6]
-
如果不考虑有几层,都需要转成一维数组的话,可以将这个参数设置为
Infinity
。 -
如果原数组有空位,
flat()
方法会跳过这个空位
[1,2,3, ,4].flat(); // [1,2,3,4]
方法flatMap()
对原数组元素每个成员执行一个函数,然后执行后的数组执行flat()
方法,这个方法也返回一个数组,不改变原数组。
[2,3,4].flatMap((x) => [x,x * 2]); // [2,4,3,6,4,8]
-
方法
flatMap()
只能展开一层数组 -
方法
flatMap()
的第二个参数用来拌定遍历函数中的this
.
数组的空位
数组的空位指的是数组某一个位置没有任何值。
例如:Array
构造函数返回的数组都是空位。
Array(2); [ , ,];
注意:空位不是
undefined
,一个位置的值等于undefined
,依然是有值的。空位是没有任何值的,in
元算符可以说明这一点。
0 in [undefined,undefined,undefined]; // true
0 in [ , , ,]; // false
在ES5中
forEach()
、filter()
、reduce()
、every()
和some()
都会跳过空位map()
会跳过空位,但是会保留这个值join()
和toString()
会将这个空位作为undefined
处理,而undefined
和null
会被处理成空字符串。
在ES6中
- 将空位转为
undefined
. Array.from()
会将数组的空位转成undefined
.- 扩展运算符(
...
)也会将空位转为undefined
. copyWithin()
会将空位一起拷贝fill()
会将空位看作正常的数组位置for...of
循环也会遍历空位entries()
、keys()
、values()
、find()
和findIndex()
会将空位处理成undefined
.
Array.prototype.sort() 的排序稳定性
排序稳定性(stable sorting)是排序算法的重要属性,指排序关键字相同的项目,排序前后的顺序不变。
常见的排序算法中,插入排序、合并排序、冒泡排序等都是稳定的,堆排序、快速排序等都是不稳定的。在***ES2019***规定,Array.prototype.sort()
的默认排序算法必须是稳定的。
备注:本文是自己学习阮一峰老师的《ECMAScript 6 入门》所做的笔记,大部分例子来源于此书。