吴文俊
吴文俊
关注前端编码,想要学习开发 Java, Android-iOS App.
中国新疆慕士塔格风景-https://pixabay.com/zh/photos/muztagh-mountain-mirror-xinjiang-9883659/

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 的标准,支持可索引的对象类:

  • Array
  • String
  • 类数组对象 TypedArray(如 Int8ArrayUint8Array 等)

请注意,在此之前有一个 .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/