新的静态 `Array` 方法
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
新的 `Array.prototype` 方法
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
Array.prototype.find(predicate, thisArg?)
Array.prototype.findIndex(predicate, thisArg?)
Array.prototype.copyWithin(target, start, end=this.length)
Array.prototype.fill(value, start=0, end=this.length)
对象 `Array` 有一些新的方法。
`Array.from()` 的基本功能是将两种类型的值转换为数组
以下是将类数组对象转换为数组的示例
`Array.from()` 也是使用 `map()` 泛型 的便捷替代方法
在此示例中,`document.querySelectorAll()` 的结果仍然是一个类数组对象,而不是一个数组,这就是我们无法在其上调用 `map()` 的原因。以前,我们为了调用 `forEach()` 而将类数组对象转换为数组。在这里,我们通过泛型方法调用和 `Array.from()` 的双参数版本跳过了该中间步骤。
`Array.from()` 的另一个用例是将类数组或可迭代值转换为 `Array` 子类的实例。例如,如果您创建了 `Array` 的子类 `MyArray` 并希望将此类对象转换为 `MyArray` 的实例,则只需使用 `MyArray.from()`。之所以可行,是因为在 ECMAScript 6 中,构造函数会相互继承(超构造函数是其子构造函数的原型)。
您还可以将此功能与映射结合使用,以获得一个可以控制结果构造函数的映射操作
物种模式允许您配置非静态内置方法(例如 `slice()`、`filter()` 和 `map()`)返回的实例。它在“类”一章的“物种模式”一节中进行了说明。
`Array.of(item_0, item_1, ···)` 创建一个数组,其元素为 `item_0`、`item_1` 等。
如果要将多个值转换为数组,则应始终使用数组字面量,尤其是在单个值为数字时,`Array` 构造函数无法正常工作的情况下(有关此怪癖的更多信息)
但是,如何将值转换为 `Array` 的子构造函数的实例呢?这就是 `Array.of()` 发挥作用的地方(请记住,`Array` 的子构造函数会继承 `Array` 的所有方法,包括 `of()`)。
数组实例可以使用几种新方法。
以下方法有助于迭代数组
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
上述每种方法的结果都是一个值序列,但它们不是作为数组返回的;它们是通过迭代器逐个显示的。让我们看一个例子。我正在使用 `Array.from()` 将迭代器的内容放入数组中
我也可以使用展开运算符 (`...`) 将迭代器转换为数组
您可以将 `entries()` 与 ECMAScript 6 的 `for-of` 循环和解构结合使用,以方便地迭代 `[index, element]` 对
Array.prototype.find(predicate, thisArg?)
返回回调函数 `predicate` 返回 `true` 的第一个数组元素。如果没有这样的元素,则返回 `undefined`。例子
Array.prototype.findIndex(predicate, thisArg?)
返回回调函数 `predicate` 返回 `true` 的第一个元素的索引。如果没有这样的元素,则返回 `-1`。例子
回调函数 `predicate` 的完整签名是
`Array.prototype.indexOf()` 的一个众所周知的限制 是它无法找到 `NaN`,因为它通过 `===` 搜索元素
使用 `findIndex()`,您可以使用 `Object.is()`(在关于 OOP 的章节中解释)并且不会遇到此类问题
您还可以采用更通用的方法,方法是创建一个辅助函数 `elemIs()`
此方法的签名是
它将索引在范围 [ `start` , `end` ) 内的元素复制到索引 `target` 和后续索引。如果两个索引范围重叠,则需要注意在覆盖所有源元素之前先复制它们。
例子
此方法的签名是
它用给定的 `value` 填充数组
(可选)您可以限制填充的开始和结束位置
空位是数组“内部”没有关联元素的索引。换句话说:如果满足以下条件,则称数组 `arr` 在索引 `i` 处有一个空位
!(i in arr)
例如:以下数组在索引 1 处有一个空位。
在本节中,您将看到许多涉及空位的示例。如果有任何不清楚的地方,您可以查阅“Speaking JavaScript”中的“数组中的空位”一节以获取更多信息。
ES6 中新增的数组方法的通用规则是:每个空位都被视为元素 `undefined`。例子
这样做的目的是引导人们远离空位并简化长期维护。不幸的是,这意味着现在情况更加不一致了。
`Array.prototype[Symbol.iterator]` 创建的迭代器将每个空位都视为元素 `undefined`。以以下迭代器 `iter` 为例
如果我们调用两次 `next()`,我们将获得索引 0 处的空位和索引 1 处的元素 `'a'`。如您所见,前者产生 `undefined`
除其他外,有两个操作基于迭代协议。因此,这些操作也将空位视为 `undefined` 元素。
首先,展开运算符 (`...`)
其次,`for-of` 循环
请注意,数组原型方法(`filter()` 等)不使用迭代协议。
如果 `Array.from()` 的参数是可迭代的,则它使用迭代将其转换为数组。然后它的工作原理与展开运算符完全相同
但 `Array.from()` 也可以将类数组对象转换为数组。然后空位也变为 `undefined`
使用第二个参数时,`Array.from()` 的工作原理与 `Array.prototype.map()` 大致相同。
但是,`Array.from()` 将空位视为 `undefined`
`Array.prototype.map()` 会跳过它们,但会保留它们
在 ECMAScript 5 中,行为已经略有不同。例如
ECMAScript 6 添加了新的行为类型
下表描述了 `Array.prototype` 方法如何处理空位。
方法 | 空位被 | |
---|---|---|
concat |
保留 | ['a',,'b'].concat(['c',,'d']) → ['a',,'b','c',,'d'] |
`copyWithin`ES6 | 保留 | [,'a','b',,].copyWithin(2,0) → [,'a',,'a'] |
`entries`ES6 | 元素 | [...[,'a'].entries()] → [[0,undefined], [1,'a']] |
every |
忽略 | [,'a'].every(x => x==='a') → true |
`fill`ES6 | 填充 | new Array(3).fill('a') → ['a','a','a'] |
filter |
移除 | ['a',,'b'].filter(x => true) → ['a','b'] |
`find`ES6 | 元素 | [,'a'].find(x => true) → undefined |
`findIndex`ES6 | 元素 | [,'a'].findIndex(x => true) → 0 |
forEach |
忽略 | [,'a'].forEach((x,i) => log(i)); → 1 |
indexOf |
忽略 | [,'a'].indexOf(undefined) → -1 |
连接 |
元素 | [,'a',undefined,null].join('#') → '#a##' |
keys ES6 |
元素 | [...[,'a'].keys()] → [0,1] |
lastIndexOf |
忽略 | [,'a'].lastIndexOf(undefined) → -1 |
映射 |
保留 | [,'a'].map(x => 1) → [,1] |
弹出 |
元素 | ['a',,].pop() → undefined |
推入 |
保留 | new Array(1).push('a') → 2 |
归约 |
忽略 | ['#',,undefined].reduce((x,y)=>x+y) → '#undefined' |
reduceRight |
忽略 | ['#',,undefined].reduceRight((x,y)=>x+y) → 'undefined#' |
反转 |
保留 | ['a',,'b'].reverse() → ['b',,'a'] |
移出 |
元素 | [,'a'].shift() → undefined |
切片 |
保留 | [,'a'].slice(0,1) → [,] |
一些 |
忽略 | [,'a'].some(x => x !== 'a') → false |
排序 |
保留 | [,undefined,'a'].sort() → ['a',undefined,,] |
拼接 |
保留 | ['a',,].splice(1,1) → [,] |
转换为字符串 |
元素 | [,'a',undefined,null].toString() → ',a,,' |
unshift |
保留 | [,'a'].unshift('b') → 3 |
values ES6 |
元素 | [...[,'a'].values()] → [undefined,'a'] |
笔记
['a',,].length → 2
const log = console.log.bind(console);
新的 ES6 操作将空位视为 undefined
元素,这有助于创建填充值的数组。
Array.prototype.fill()
用固定值替换所有数组元素(包括空位)
new Array(3)
创建一个包含三个空位的数组,fill()
用值 7
替换每个空位。
Array.prototype.keys()
即使数组只有空位也会报告键。它返回一个可迭代对象,您可以通过扩展运算符将其转换为数组
Array.from()
的第二个参数中的映射函数会收到有关空位的通知。因此,您可以使用 Array.from()
进行更复杂的填充
undefined
填充 如果您需要一个填充了 undefined
的数组,则可以使用迭代(由扩展运算符触发)将空位转换为 undefined
的事实
ES5 方法 filter()
允许您删除空位
ES6 迭代(通过扩展运算符触发)允许您将空位转换为 undefined
元素
concat()
展开 (Symbol.isConcatSpreadable
) 您可以通过添加一个(自身或继承的)属性来配置 Array.prototype.concat()
如何处理对象,该属性的键是众所周知的符号 Symbol.isConcatSpreadable
,其值为布尔值。
默认情况下,Array.prototype.concat()
将数组*展开*到其结果中:它们的索引元素成为结果的元素
使用 Symbol.isConcatSpreadable
,您可以覆盖默认值并避免数组的展开
对于非数组,默认情况下不展开
您可以使用 Symbol.isConcatSpreadable
强制展开
concat()
如何确定参数是否为数组?它使用与 Array.isArray()
相同的算法。Array.prototype
是否在原型链中对该算法没有影响。这很重要,因为在 ES5 及更早版本中,使用了一些技巧来子类化 Array
,并且这些技巧必须继续有效(请参阅本书中有关 __proto__
的部分)
Symbol.isConcatSpreadable
ES6 标准库中没有对象的属性键为 Symbol.isConcatSpreadable
。因此,此机制纯粹是为了浏览器 API 和用户代码而存在的。
结果
Array
的子类默认情况下会被展开(因为它们的实例是数组对象)。Array
的子类可以通过将键为 Symbol.isConcatSpreadable
的属性设置为 false
来阻止其实例被展开。该属性可以是原型属性或实例属性。[Symbol.isConcatSpreadable]
为 true
,则其他类似数组的对象会被 concat()
展开。例如,这将允许为某些类似数组的 DOM 集合启用展开。concat()
方法。对于数组,ES6 仍然具有与 ES5 相同的规则
l
在 0 ≤ l
≤ 232−1 的范围内。i
在 0 ≤ i
< 232−1 的范围内。字符串和类型化数组的索引范围更大:0 ≤ i
< 253−1。该范围的上限是由于 253−1 是 JavaScript 的浮点数可以安全表示的最大整数。有关详细信息,请参阅“安全整数”部分。
普通数组的索引范围较小的唯一原因是向后兼容性。