How JavaScript’s at() method makes array indexing easier
Arrays are an everyday thing for front-end developers: rendering lists, managing state, juggling DOM elements. Getting the last item has always been clunkier than it should be. Array.prototype.at() makes it easy.
What’s at()?
The at() method is a relatively new addition to JavaScript (introduced in ECMAScript 2022), and it’s designed to make accessing array elements more readable, particularly from the end of an array.
const fruits = ['apple', 'banana', 'cherry'];
console.log(fruits.at(0)); // 'apple'
console.log(fruits.at(-1)); // 'cherry'
Compare that to the older approach we’ve always used:
console.log(fruits[fruits.length - 1]); // 'cherry'
.at(-1) is cleaner, and there’s no length - 1 to get wrong.
Why should you use at()?
- Negative indexing is the big one.
.at(-1)gets the last item without calculating.length - 1. - It reads better, especially with stacks, queues, or undo/redo arrays.
- It also works on strings and typed arrays.
const greeting = 'Hello';
console.log(greeting.at(-1)); // 'o'
const int8 = new Int8Array([1, 2, 3]);
console.log(int8.at(-1)); // 3
Comparing with bracket notation
| Access Style | Code | Output |
|---|---|---|
| Bracket notation | arr[arr.length - 1] |
"cherry" |
at() method |
arr.at(-1) |
"cherry" |
Both methods return the same result, and both return undefined for out-of-bounds indices.
Edge cases to keep in mind
Like bracket notation, .at() behaves predictably at the edges, but a few details are worth knowing:
const nums = [10, 20];
console.log(nums.at(5)); // undefined
console.log(nums.at(-5)); // undefined
console.log(nums.at(1.5)); // 20 (1.5 is truncated to 1, not rounded)
console.log(nums.at(2.5)); // undefined (2.5 becomes 2, which is out of bounds)
.at()uses truncation, not rounding. Internally, it’s equivalent toMath.trunc(index).- If the resulting index is out of bounds, positive or negative, it returns
undefined.
Browser support
The .at() method is supported in all modern browsers (Chrome 92+, Firefox 90+, Safari 15+, Edge 92+), but not in Internet Explorer. If you’re still supporting legacy environments, consider adding this simple polyfill:
if (!Array.prototype.at) {
Array.prototype.at = function (n) {
if (this == null) throw new TypeError('Called on null or undefined');
const len = this.length >>> 0;
n = Number(n);
if (isNaN(n)) n = 0;
n = n < 0 ? Math.ceil(n) : Math.floor(n); // manual truncation
if (n < 0) n += len;
if (n < 0 || n >= len) return undefined;
return this[n];
};
}
This polyfill avoids using Math.trunc() for maximum compatibility.
Use cases in the wild
A few places where at() is handy:
Getting the last message
const messages = ['Hi', 'How are you?', 'See you soon'];
const latest = messages.at(-1); // 'See you soon'
Navigating a carousel
const images = ['img1.png', 'img2.png', 'img3.png'];
const prev = images.at(-2); // 'img2.png'
Peeking at the top of a stack
const historyStack = ['/home', '/about', '/contact'];
const current = historyStack.at(-1); // '/contact'
If you’ve worked with Python or Ruby, this will feel familiar. list[-1] in Python and array[-1] in Ruby do the same thing that at(-1) does here.
It’s a small addition, but I like it. at(-1) is shorter, easier to read, and there’s one less off-by-one bug to worry about.