数组的扩展
1. 扩展运算符
1)含义
扩展运算符(spread)是三个点(...
)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
扩展运算符后面还可以放置表达式。
如果扩展运算符后面是一个空数组,则不产生任何效果。
注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
2)替代函数的 apply 方法
由于扩展运算符可以展开数组,所以不再需要apply
方法,将数组转为函数的参数了。
通过push
函数,将一个数组添加到另一个数组的尾部。
上面代码的 ES5
写法中,push
方法的参数不能是数组,所以只好通过apply
方法变通使用push
方法。有了扩展运算符,就可以直接将数组传入push
方法。
3)扩展运算符的应用 *
(1)复制数组
数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。
ES5
只能用变通方法来复制数组。
扩展运算符提供了复制数组的简便写法。
(2)合并数组
扩展运算符提供了数组合并的新写法。
不过,这两种方法都是浅拷贝,使用的时候需要注意。
上面代码中,a3
和a4
是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了引用指向的值,会同步反映到新数组。
(3)与结构赋值结合
扩展运算符可以与解构赋值结合起来,用于生成数组。
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
(4)字符串
扩展运算符还可以将字符串转为真正的数组。
上面的写法,有一个重要的好处,那就是能够正确识别四个字节的 Unicode 字符。
(5)实现了 Iterator 接口的对象
任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。
对于那些没有部署 Iterator 接口的类似数组的对象,扩展运算符就无法将其转为真正的数组。
上面代码中,arrayLike
是一个类似数组的对象,但是没有部署 Iterator 接口,扩展运算符就会报错。这时,可以改为使用Array.from
方法将arrayLike
转为真正的数组。
(6)Map 和 Set 结构, Generator函数
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。
Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
2. Array.from()
1)含义
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
2)用法
把一个类似数组的对象,Array.from
将它转为真正的数组。
实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments
对象。Array.from
都可以将它们转为真正的数组。
只要是部署了 Iterator 接口的数据结构,Array.from
都能将其转为数组。字符串和 Set 结构都具有 Iterator 接口,因此可以被Array.from
转为真正的数组。
参数是一个真正的数组,Array.from
会返回一个一模一样的新数组。
扩展运算符背后调用的是遍历器接口(Symbol.iterator
),如果一个对象没有部署这个接口,就无法转换。Array.from
方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length
属性。因此,任何有length
属性的对象,都可以通过Array.from
方法转为数组,而此时扩展运算符就无法转换。
Array.from
还可以接受第二个参数,作用类似于数组的map
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
将数组中布尔值为false
的成员转为0
。
返回各种数据的类型。
3)模拟实现
对于还没有部署该方法的浏览器,可以用Array.prototype.slice
方法替代。
3. Array.of()
1) 含义
Array.of
方法用于将一组值,转换为数组。
2)用法
这个方法的主要目的,是弥补数组构造函数Array()
的不足。因为参数个数的不同,会导致Array()
的行为有差异。
只有当参数个数不少于 2 个时,Array()
才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度。
Array.of
总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
3)模拟实现
Array.of
方法可以用下面的代码模拟实现。
4. 数组实例的 copyWith()
1)含义
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
2)用法
它接受三个参数。
target(必需):从该位置开始替换数据。如果为负值,表示倒数。
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
这三个参数都应该是数值,如果不是,会自动转为数值。
5. 数组实例的 find() 和 findIndex()
1)find 含义
数组实例的find
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
。
2)find 用法
find
方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
3)findIndex 含义
返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
。
4)用法
这两个方法都可以接受第二个参数,用来绑定回调函数的this
对象。
这两个方法都可以发现NaN
,弥补了数组的indexOf
方法的不足。
indexOf
方法无法识别数组的NaN
成员,但是findIndex
方法可以借助Object.is
方法做到。
6. 数组实例的 fill()
1)含义
fill
方法使用给定值,填充一个数组。
上面代码表明,fill
方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
2)用法
fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
7. 数组实例的 entries(), keys(),和values()
1)含义
keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
如果不使用for...of
循环,可以手动调用遍历器对象的next
方法,进行遍历。
8. 数组实例的 includes()
1)含义
Array.prototype.includes
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似。
2)用法
该方法的第二个参数表示搜索的起始位置,默认为0
。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4
,但数组长度为3
),则会重置为从0
开始。
没有该方法之前,我们通常使用数组的indexOf
方法,检查是否包含某个值。
indexOf
方法有两个缺点
不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于
-1
,表达起来不够直观。它内部使用严格相等运算符(
===
)进行判断,这会导致对NaN
的误判。
includes
使用的是不一样的判断算法,就没有这个问题。
Map 和 Set 数据结构有一个has
方法,需要注意与includes
区分。
Map 结构的
has
方法,是用来查找键名的,比如Map.prototype.has(key)
、WeakMap.prototype.has(key)
、Reflect.has(target, propertyKey)
。Set 结构的
has
方法,是用来查找值的,比如Set.prototype.has(value)
、WeakSet.prototype.has(value)
。
9. 数组实例的 flat(), flatMap()
1)flat() 含义
数组的成员有时还是数组,Array.prototype.flat()
用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
2)flat() 用法
flat()
默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()
方法的参数写成一个整数,表示想要拉平的层数,默认为1。参数为2,表示要“拉平”两层的嵌套数组。
如果不管有多少层嵌套,都要转成一维数组,可以用Infinity
关键字作为参数。
如果原数组有空位,flat()
方法会跳过空位。
3) flatMap() 含义
flatMap()
方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()
),然后对返回值组成的数组执行flat()
方法。该方法返回一个新数组,不改变原数组。
flatMap()
只能展开一层数组,返回的还是一个嵌套数组。
4)flatMap() 用法
flatMap()
方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。
10. 数组的空位
1)含义
数组的空位指,数组的某一个位置没有任何值。比如,Array
构造函数返回的数组都是空位。
注意,空位不是undefined
,一个位置的值等于undefined
,依然是有值的。空位是没有任何值,in
运算符可以说明这一点。
2)用法
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
。
由于空位的处理规则非常不统一,所以建议避免出现空位。
11. Array.prototype.sort() 的排序稳定性
2)含义
排序稳定性(stable sorting)是排序算法的重要属性,指的是排序关键字相同的项目,排序前后的顺序不变。
上面代码对数组arr
按照首字母进行排序。排序结果中,straw
在spork
的前面,跟原始顺序一致,所以排序算法stableSorting
是稳定排序。
上面代码中,排序结果是spork
在straw
前面,跟原始顺序相反,所以排序算法unstableSorting
是不稳定的。
最后更新于
这有帮助吗?