2021.04.27(数组实例的 includes()、数组实例的 flat(),flatMap()、数组的空位、Array.prototype.sort() 的排序稳定性)
数组实例的 includes()
Array.prototype.includes 方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串
的 includes 方法类似。ES2016 引入了该方法。
1. [1, 2, 3].includes(2) // true 2. [1, 2, 3].includes(4) // false 3. [1, 2, NaN].includes(NaN) // true
该方法的第二个参数表示搜索的起始位置,默认为 0 。如果第二个参数为负数,则表示倒数的位置,
如果这时它大于数组长度(比如第二个参数为 -4 ,但数组长度为 3 ),则会重置为从 0 开始。
1. [1, 2, 3].includes(3, 3); // false 2. [1, 2, 3].includes(3, -1); // true
没有该方法之前,我们通常使用数组的 indexOf 方法,检查是否包含某个值。
1. if (arr.indexOf(el) !== -1) { 2. // ... 3. }
indexOf 方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比
较是否不等于 -1 ,表达起来不够直观。二是,它内部使用严格相等运算符( === )进行判断,这
会导致对 NaN 的误判。
1. [NaN].indexOf(NaN) 2. // -1
includes 使用的是不一样的判断算法,就没有这个问题。
1. [NaN].includes(NaN) 2. // true
下面代码用来检查当前环境是否支持该方法,如果不支持,部署一个简易的替代版本。
1. const contains = (() => 2. Array.prototype.includes 3. ? (arr, value) => arr.includes(value) 4. : (arr, value) => arr.some(el => el === value) 5. )(); 6. contains(['foo', 'bar'], 'baz'); // => false
另外,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) 。
数组实例的 flat(),flatMap()
数组的成员有时还是数组, Array.prototype.flat() 用于将嵌套的数组“拉平”,变成一维的数
组。该方法返回一个新数组,对原数据没有影响。
1. [1, 2, [3, 4]].flat() 2. // [1, 2, 3, 4]
上面代码中,原数组的成员里面有一个数组, flat() 方法将子数组的成员取出来,添加在原来的位
置。
flat() 默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将 flat() 方法的参数写成
一个整数,表示想要拉平的层数,默认为1。
1. [1, 2, [3, [4, 5]]].flat() 2. // [1, 2, 3, [4, 5]] 3. 4. [1, 2, [3, [4, 5]]].flat(2) 5. // [1, 2, 3, 4, 5]
上面代码中, flat() 的参数为2,表示要“拉平”两层的嵌套数组。
如果不管有多少层嵌套,都要转成一维数组,可以用 Infinity 关键字作为参数。
1. [1, [2, [3]]].flat(Infinity) 2. // [1, 2, 3]
如果原数组有空位, flat() 方法会跳过空位。
1. [1, 2, , 4, 5].flat() 2. // [1, 2, 4, 5]
flatMap() 方法对原数组的每个成员执行一个函数(相当于执行 Array.prototype.map() ),然
后对返回值组成的数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。
1. // 相当于 [[2, 4], [3, 6], [4, 8]].flat() 2. [2, 3, 4].flatMap((x) => [x, x * 2]) 3. // [2, 4, 3, 6, 4, 8]
flatMap() 只能展开一层数组。
1. // 相当于 [[[2]], [[4]], [[6]], [[8]]].flat() 2. [1, 2, 3, 4].flatMap(x => [[x * 2]]) 3. // [[2], [4], [6], [8]]
上面代码中,遍历函数返回的是一个双层的数组,但是默认只能展开一层,因此 flatMap() 返回的
还是一个嵌套数组。
flatMap() 方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数
组成员的位置(从零开始)、原数组。
1. arr.flatMap(function callback(currentValue[, index[, array]]) { 2. // ... 3. }[, thisArg])
flatMap() 方法还可以有第二个参数,用来绑定遍历函数里面的 this 。
数组的空位
数组的空位指,数组的某一个位置没有任何值。比如, Array 构造函数返回的数组都是空位。
1. Array(3) // [, , ,]
上面代码中, Array(3) 返回一个具有 3 个空位的数组。
注意,空位不是 undefined ,一个位置的值等于 undefined ,依然是有值的。空位是没有任何
值, in 运算符可以说明这一点。
1. 0 in [undefined, undefined, undefined] // true 2. 0 in [, , ,] // false
ES6 则是明确将空位转为 undefined 。
Array.prototype.sort() 的排序稳定性
排序稳定性(stable sorting)是排序算法的重要属性,指的是排序关键字相同的项目,排序前后的
顺序不变。
1. const arr = [ 2. 'peach', 3. 'straw', 4. 'apple', 5. 'spork' 6. ]; 7. 8. const stableSorting = (s1, s2) => { 9. if (s1[0] < s2[0]) return -1; 10. return 1; 11. }; 12. 13. arr.sort(stableSorting) 14. // ["apple", "peach", "straw", "spork"]
上面代码对数组 arr 按照首字母进行排序。排序结果中, straw 在 spork 的前面,跟原始顺序
一致,所以排序算法 stableSorting 是稳定排序。
1. const unstableSorting = (s1, s2) => { 2. if (s1[0] <= s2[0]) return -1; 3. return 1; 4. }; 5. 6. arr.sort(unstableSorting) 7. // ["apple", "peach", "spork", "straw"]
上面代码中,排序结果是 spork 在 straw 前面,跟原始顺序相反,所以排序算
法 unstableSorting 是不稳定的。
ES2019 明确规定, Array.prototype.sort() 的默认
排序算法必须稳定。这个规定已经做到了,现在 JavaScript 各个主要实现的默认排序算法都是稳定
的。
2021-04-27 14:21:07