ECMAScript 提案:Array, 类数组和字符串的 .item() 方法
由 Shu-yu Guo 和 Tab Atkins 提出的 ECMAScript 提案“.item()” 为可索引对象(Array, 类数组,string)推出的方法。给定一个索引,该方法返回符合的值。该方法的关键好处是索引可以是负值(-1
获取最后一个元素,-2
对应倒数第二个元素,等等)。
2020 年 11 月更新:名为 .item()
的方法已经结束支持。实验性的新名称是 .at()
可索引类的方法 item()
方法 .item()
在数组中有以下用法:
const arr = ['a', 'b', 'c', 'd']
assert.equal(arr.item(1), 'b')
assert.equal(arr.item(0), 'a')
assert.equal(arr.item(-1), 'd')
于是,下面的两个表达式是等价的:
arr.item(-1)
arr[arr.length - 1]
前面两行展示了 .item()
的关键好处:我们可以使用负索引值来访问一个 Array 末端的元素。其它的 Array 方法如 .slice()
已经支持负指数(而方括号操作符 []
不支持)。
;['a', 'b', 'c', 'd'].slice(1, -1)
// -> [ 'b', 'c' ]
;['a', 'b', 'c', 'd'].slice(-1)
// -> [ 'd' ]
索引溢出
如果索引溢出,使用方括号操作符访问溢出索引值属性的值:
// Set up an Array
const arr = []
arr['4294967296'] = 'abc'
// `arr` has no indexed properties
assert.equal(arr.length, 0)
// Index 4294967296 is out of bounds
assert.equal(arr[4294967296], 'abc')
数组索引范围 是 [0, 2^32 − 1)(最大数组长度 2^32 − 1 不包含在内)。
相比之下,.item()
在上面的用例中返回 undefined
:
assert.equal(arr.item(4294967296), undefined)
这使得程序索引安全一些。
拥有方法 .item()
的类
提案是添加方法 .item()
到可索引类和 ECMAScript 原始类型:
- Array
- 所有的类数组:
Uint8Array
等 - string
此外,几个 DOM 类同样拥有该方法:
- HTMLCollection(动态的,由
.getElementsByClassName()
,.getElementsByTagName()
等返回)。 - NodeList(静态的,由
.querySelectorAll()
返回)。 - DOMTokenList(静态的,
.classList
的值)。 - 其它情况
访问数组末端元素 - 可用于替换 .item()
当想要访问负值索引,有一些替换 .item()
的方法,但是笨拙且难用:
const arr = ['a', 'b', 'c', 'd']
const N = -2
const element1 = arr[arr.length + N]
assert.equal(element1, 'c')
const element2 = arr.slice(N)[0]
assert.equal(element2, 'c')
const { length, [length + N]: element3 } = arr
assert.equal(element3, 'c')
当我们需要获取数组的末端元素,并且不介意删除它的时候,也可以使用 .pop()
:
const lastElement = arr.pop()
assert.equal(lastElement, 'd')
.item()
polyfill
这有我们可以填充使用 .item()
(一份频繁编辑版本 code shown in the proposal):
function item(n) {
// ToInteger() abstract operation
n = Math.trunc(n) || 0
// Allow negative indexing from the end
if (n < 0)
n += this.length
// Out-of-bounds access is guaranteed to return undefined
if (n < 0 || n >= this.length)
return undefined
// Otherwise, this is just normal property access
return this[n]
}
// Other TypedArray constructors omitted for brevity.
for (const C of [Array, String, Uint8Array]) {
Object.defineProperty(C.prototype, 'item', {
value: item,
writable: true,
enumerable: false,
configurable: true,
})
}
npm 包填充器
下面两个 npm 包提供支持:
.item()
和升级中的可索引 DOM 集合
目前 DOM 的一个计划是将现有的和即将到来的可索引 DOM 集合(如 HTMLCollection
和 NodeList
)基于 ObservableArray
。该类的实例是数组的代理,因此具有 .map()
等方法,而这些方法目前在可索引 DOM 数据结构中是不可用的。那么在使用这些方法之前,将不再需要将这些集合转换为数组:
// Old:
;[...document.querySelectorAll('img')].map(img => img.src)
// New:
document.querySelectorAll('img').map(img => img.src)
所有可索引的 DOM 数据结构都有 .item()
这个方法。由于各种原因,使 ObservableArray
与它们兼容的最简单的方法,是将这个方法添加到 Array
中。
例子:与 .replace()
回调函数一起使用
下面的代码显示 .item()
是有用的:
const result = 'first=jane, last=doe'.replace(
/(?<key>[a-z]+)=(?<value>[a-z]+)/g,
(...args) => {
const groups = args.item(-1) // (A)
const { key, value } = groups
return `${key.toUpperCase()}=${value.toUpperCase()}`
}
)
assert.equal(result, 'FIRST=JANE, LAST=DOE')
groups
一直是 .replace()
回调函数的最后一个参数。如果在行 A 中 .item()
不可用,同样有效的是:
const groups = args.pop()
问题解释 FAQ
为什么不允许在括号中使用负值索引?
不幸的是,JavaScript 不会改变以允许在方括号中使用负值索引。问题是因为这么做会破环现有代码。
我们来看一些代码。我不推荐使用这些技术,但是相似的代码存在于网络上。如果负值索引被允许在方括号中使用,每种情况都会被破坏。
第一个例子:
const english = ['hello', 'world']
const german = ['hallo', 'Welt']
function translate(word) {
return german[english.indexOf(word)]
}
assert.equal(translate('world'), 'Welt')
assert.equal(translate('universe'), undefined)
第二个例子:
const arr = ['fee', 'fi', 'fo', 'fum']
arr['-1'] = 'Englishman'
// Current behavior:
assert.equal(arr[-1], 'Englishman')
第三个例子:
const numbers = [1, 2, 3]
const reversed = []
let i = numbers.length - 1
while (numbers[i]) {
reversed.push(numbers[i--])
}
assert.deepEqual(reversed, [3, 2, 1])
为什么不是 .getItem()
和 .setItem()
正如在章节“.item() 和升级中的可索引 DOM 集合”中解释过,方法名 .item()
有助于升级 DOM。这就是为什么这个名字比其它名字更受欢迎。
什么是 ECMAScript 提案 .last
?
也有 stage 1 提案 a getter/setter .last。然而,引用该提案:
其它提案(Array.prototype.item 和 Array Slice Notation)也充分解决这个问题,并在标准轨道上更快地推进。如果其中一个提案进入 stage 3,这个提案将被放弃。
资源
这篇文章引用的资源有:
本文由 吳文俊 翻译,原文地址 ECMAScript proposal: Method .item() for Arrays, Typed Arrays, and strings