简介
开发中经常有访问数组最后一个元素的需要,相信大家和我一样,用的最多的应该是 索引操作 吧,对于一个 长度为 n 的数组 arr 来说,我们一般会使用 arr[n - 1] 来访问其最后一个元素。
可以看出通过索引的方法从数组后面开始取元素多少还是有些不方便的,那么除了这个方法以外,还有什么方法能够访问数组的最后一个元素呢?
接下来介绍的主角就是 Array.prototype.at() 方法了,这是 ES2022 新登场的 api。
Array.prototype.at()
at() 方法接收一个 整数值 并返回该索引的项目,允许正数和负数。负整数从数组中的最后一个项目开始倒数。如果找不到指定的索引,则返回 undefined
更多关于 Array.prototype.at():Array.prototype.at() - JavaScript | MDN (mozilla.org)
示例
我们可以通过 at 来访问数组 arr,与索引访问类似,传递一个索引值,但不同的地方在于,我们通过索引访问时,如果想访问最后一个元素,只能通过 arr[arr.length - 1] 这种形式,但是 at 的参数可以接受一个负数,表示从最后一个元素开始。
const arr = [1, 2, 3, 4, 5];
console.log(arr.at(1)) // 1
console.log(arr.at(-1)) // 5
可以看到,at 方法可以通过传递一个负数从尾部来访问数组,从 可读性 的角度来看,还是优于通过索引访问的。
那么有了 Array.prototype.at() 这个新宠儿,是不是以后类似场景就不需要索引操作了呢?我们来看看它的兼容性:
接下来再看看 at 和索引操作的效率对比。
效率对比
少数据量
at
let arr = new Array(100).fill(0).map((v, i) => i);
console.time('at');
for (let i = 0; i < 1000; i++) {
arr.at(-1);
}
console.timeEnd('at'); // at: 0.156ms
index
let arr = new Array(100).fill(0).map((v, i) => i);
console.time('index');
for (let i = 0; i < 1000; i++) {
arr[arr.length - 1];
}
console.timeEnd('index'); // index: 0.104ms
可以发现通过索引的方式访问效率还是高一点的。
较多数据量
at
let arr = new Array(100000).fill(0).map((v, i) => i);
console.time('at');
for (let i = 0; i < 1000; i++) {
arr.at(-1);
}
console.timeEnd('at'); // at: 0.172ms
index
let arr = new Array(100000).fill(0).map((v, i) => i);
console.time('index');
for (let i = 0; i < 1000; i++) {
arr[arr.length - 1];
}
console.timeEnd('index'); // index: 0.159ms
可以发现通过索引的方式访问效率 仍然快于 通过 at 去访问。
同时我们能发现 at 的速度在数据量 100000 的情况下,和数据量 100 的时候也差不会太多,这是不是说明 at 实际上也是通过操作索引来访问的数组呢?
at 的实现原理
我们来看看规范里是怎么实现 Array.prototype.at 的:
1. Let `O` be ? ToObject(this value).
2. Let `len` be ? LengthOfArrayLike(O).
3. Let `relativeIndex` be ? ToIntegerOrInfinity(`index`).
4. If `relativeIndex` ≥ 0, then
a. Let `k` be `relativeIndex`.
5. Else,
a. Let `k` be `len` + `relativeIndex`.
6. If `k` < 0 or `k` ≥ `len`, return undefined.
7. Return ? Get(`O`, !ToString(k)).
我们先来看看第 4 点,如果传入的参数 k >= 0,那么这个时候 at 方法的表现形式和通过索引访问数组的表现形式是一致的。而负数索引的相关内容在第 5 点体现,如果传入的参数 k < 0,则最终的索引值为 length + k。
因此,at 方法相当于帮我们省了点事儿,也正因为它帮我们省的这些事儿,导致了它相较于通过索引直接访问数组的效率来说更慢些:更多的处理、判断、运算、函数上下文等等。
也正是因为 at 方法同样适用索引访问数组,使得它的效率没有想象中那么低。
结语
这是 《你不知道的Javascript》 专栏的第一篇文章。本专栏致力于剖析 Javascript 的本质,带着大家发现不一样的 Javascript。