Array、类数组和字符串的 `.at()` 方法
JavaScript 的 .at() 方法为数组、类数组对象(如 TypedArray)和字符串提供简洁的方式访问特定索引的元素。给定一个索引,返回一个符合的结果。该方法的好处是支持负数索引,允许从末尾访问元素(-1 获取最后一个元素,-2 返回倒数第二个元素)。
方法 at()
方法 at() 有以下用法:
const arr = ['a', 'b', 'c', 'd'];
console.log(arr.at(0)); // 'a'
console.log(arr.at(2)); // 'c'
console.log(arr.at(-1)); // 'd'
const str = 'hello';
console.log(str.at(1)); // 'e'
console.log(str.at(-2)); // 'l'
下面的表达式是等价的:
arr.at(-1);
arr[arr.length - 1];
方法 at() 与许多 Array 方法(如 slice()、with())类似,支持负数索引。
arr.slice(1, -1); // ['b', 'c']
arr.with(-1, 'z'); // ['a', 'b', 'c', 'z']
支持 at() 方法的对象类
at() 方法是 ECMAScript 的标准,支持可索引的对象类:
ArrayString- 类数组对象
TypedArray(如Int8Array、Uint8Array等)
请注意,在此之前有一个 .item() 方法的提案,但由于与已经存在的 JS 库不兼容(例如 YUI2 和 YUI3),最终改名为 .at()。但 .item() 仍然存在于下面的 Web API 中:
NodeList:由document.querySelectorAll()返回的节点列表HTMLCollection:由document.getElementsByTagName()、.getElementsByClassName()返回的 HTML 元素集合DOMTokenList:如element.classList返回的类列表
截至 2026 年 2 月 4 日,这些 Web API 对象尚未实现 at() 方法。
polyfill 填充实现
我们编写一份粗略版本,用于在不支持 at() 方法的环境中填充该功能(不能保证与规范一致,如在 undefined 上调用):
function at(n) {
// ToInteger() abstract op
n = Math.trunc(n) || 0;
// Allow negative indexing from the end
if (n < 0) n += this.length;
// OOB access is guaranteed to return undefined
if (n < 0 || n >= this.length) return undefined;
// Otherwise, this is just normal property access
return this[n];
}
for (const C of [Array, String, Int8Array, Uint8Array]) {
Object.defineProperty(C.prototype, 'at', {
value: at,
writable: true,
enumerable: false,
configurable: true,
});
}
使用 .at()
在使用 .at() 方法时,需要注意索引边界,超出边界会返回 undefined。
const arr = [1, 2];
console.log(arr.at(10)); // undefined
console.log(-10); // undefined
Unicode 字符串中,某些字符可能由多个代码单元组成,使用 .at() 访问这些字符时需要小心:
const str = '𝟘𝟙𝟚';
console.log(str.at(0)); // '\uD835'
const emoji = '👨👩👧👦';
console.log(emoji.at(0)); // '\uD83D' (乱码,只是这个复杂 emoji 的一部分)
可以与 .replace() 等字符串方法结合使用:
const result = 'first=jane, last=doe'.replace(
/(?<key>[a-z]+)=(?<value>[a-z]+)/g,
(...args) => {
const groups = args.at(-1);
const { key, value } = groups;
return key.toUpperCase() + '=' + value.toUpperCase();
},
);
assert.equal(result, 'FIRST=JANE, LAST=DOE');
为何不在数组或者字符串的 [] 括号内使用负值索引
遗憾的是 JavaScript 有许多已经存在的行为特性无法改变,例如:
.indexof() 方法在不存在的参数时返回 -1
const arr = ['a', 'b'];
arrA.indexOf('c'); // -1
负值字符串(例 "-1"")可以作为对象的属性名使用,[-1] 负值会是一个对象属性:
const fruits = ['苹果', '香蕉', '李子'];
fruits['-1'] = '土豆'; // fruits: ['苹果', '香蕉', '李子', -1: '土豆']
console.log(fruits[-1]); // '土豆'
另外,负值作为索引属于索引越界,在过去返回值会是 undefined,在已有程序中存在大量相关代码,如果允许负值索引允许在括号中使用会破坏:
const numbers = [1, 2, 3, 4];
if (numbers[N]) {
// do...
}
let i = numbers.length - 1;
while (numbers[i]) {
// do...
}
结语
方法 .at() 是 ES2022 提案的特性,比传统的 arr[arr.length - 1] 语法更简洁易读,在读取数组、字符串的元素时有了更好的选择。
本文作者 吴文俊,转载请注明来源链接:
本文链接: https://tie.pub/blog/at/