Array.at(-1) - Elegant Last Element Access
Get the last array element without calculating length. Negative indices that actually work.
Finally, something that feels like Python in JavaScript. No more
array[array.length - 1].
Live demo
Add or remove items to see how at(-1) always returns the last element.
items[items.length - 1]ElderberryVerbose. Have to calculate index. Ugly.
items.at(-1)ElderberryClean. Readable. Works like Python.
Negative indices
at() supports negative indices. Count from the end of the array.
| Expression | Result | Explanation |
|---|---|---|
items.at(-1) | Elderberry | Last element |
items.at(-2) | Date | Second to last |
items.at(-3) | Cherry | Third to last |
items.at(0) | Apple | First element (same as items[0]) |
Practical use cases
Get the last item
// Old way
const lastItem = items[items.length - 1];
// New way
const lastItem = items.at(-1);Get the last N items
// Old way
const lastTwo = items.slice(items.length - 2);
// New way
const lastTwo = items.slice(-2);
// Get second-to-last
const secondLast = items.at(-2);Safe access (returns undefined if out of bounds)
const empty = [];
// Old way - throws error or needs guard
const last = empty.length > 0 ? empty[empty.length - 1] : undefined;
// New way - returns undefined automatically
const last = empty.at(-1); // undefined💡 Browser support: Chrome 92+, Firefox 90+, Safari 15.4+. Node.js 16.6.0+. All modern environments since 2021.
What is Array.at()?
Array.prototype.at() returns the element at a given index.
The useful part: it supports negative indices.
const items = ["a", "b", "c", "d"]; items.at(-1); // "d" (last element) items.at(-2); // "c" (second-to-last) items.at(0); // "a" (first element, same as items[0])
Negative indices count from the end of the array. -1 is last, -2 is second-to-last, and so on.
Why this matters
Getting the last element of an array has always been awkward in JavaScript:
// Old way const last = items[items.length - 1];
This works, but:
- Verbose - you repeat
itemstwice - Noisy -
[items.length - 1]does not exactly read "last item" - Easy to get wrong -
- 2instead of- 1, or a copy-paste of the wrong variable
With at():
// New way const last = items.at(-1);
Clean. Readable. Does what it says.
Basic usage
Get the last element
const fruits = ["apple", "banana", "cherry"]; fruits.at(-1); // "cherry"
Same as:
fruits[fruits.length - 1]; // "cherry"
Just shorter and clearer.
Get the second-to-last element
fruits.at(-2); // "banana"
Same as:
fruits[fruits.length - 2]; // "banana"
Positive indices still work
fruits.at(0); // "apple" fruits.at(1); // "banana"
Same as:
fruits[0]; // "apple" fruits[1]; // "banana"
at() is not only about negative indices - it just makes those nicer.
Real-world use cases
Get the last item in a list
Before:
const messages = getMessages(); const lastMessage = messages[messages.length - 1];
After:
const messages = getMessages(); const lastMessage = messages.at(-1);
Get the most recent entry
const history = getUserHistory(); const mostRecent = history.at(-1);
Handy for undo/redo stacks, navigation history, or any ordered list where you care about the last thing that happened.
Compare first and last
const scores = [85, 90, 78, 92, 88]; const firstScore = scores.at(0); const lastScore = scores.at(-1); if (lastScore > firstScore) { console.log("Improvement!"); }
Symmetric and easy to read. No [0] versus [scores.length - 1] asymmetry.
Safe access to dynamic arrays
at() returns undefined if the index is out of bounds:
const empty = []; empty.at(-1); // undefined (no error) empty.at(10); // undefined
With bracket notation you often end up writing guards like:
const last = items.length > 0 ? items[items.length - 1] : undefined;
With at():
const last = items.at(-1); // undefined if empty
Same result, less ceremony.
When NOT to use at()
at() is great, but not a replacement for every [].
Skip it for:
- Simple positive indices -
items[0]is perfectly fine - Looping -
items[i]inside aforloop is idiomatic and faster - Tight hot paths -
at()is a method call, bracket access is as direct as it gets
Use it for:
- Negative indices -
items.at(-1)beatsitems[items.length - 1]every time - Code where readability matters more than micro-optimisation
- APIs that accept "relative from end" indices
If you ever write array[array.length - X], it is at least worth asking if array.at(-X) would be clearer.
Comparison with other patterns
vs. slice(-1)
You can get the last element with slice():
const last = items.slice(-1)[0];
This works, but:
- More verbose - extra
slice()call and[0] - Less efficient - creates a new array
- Less obvious - that trailing
[0]is easy to miss
at(-1) is simpler:
const last = items.at(-1);
vs. pop()
pop() removes and returns the last element:
const last = items.pop();
This mutates the array. If you only want to read the last element, use at(-1):
const last = items.at(-1); // read-only
Mutating and reading are very different operations; pop() is for stacks, at() is for access.
Works with strings too
String.prototype.at() behaves the same way:
const text = "hello"; text.at(-1); // "o" text.at(-2); // "l" text.at(0); // "h"
Same relative indexing, just on characters.
Browser support
Array.prototype.at() is supported in:
- Chrome 92+
- Firefox 90+
- Safari 15.4+
- Edge 92+
- Node.js 16.6.0+
In other words: anything reasonably current.
Polyfill
If you need to support older browsers, you can polyfill:
if (!Array.prototype.at) { Array.prototype.at = function (index) { const i = index < 0 ? this.length + index : index; return this[i]; }; }
Or pull it from a polyfill library like core-js:
import "core-js/actual/array/at";
Performance
Under the hood, at() is a tiny bit slower than direct bracket access because it is a method call that does a range check for you.
For everyday code, the difference is in the noise - network calls, DOM updates, and database queries will dominate long before at() does.
If you are writing a tight inner loop over millions of items in performance-critical code, use bracket notation. If you are writing normal application logic, prefer the version that is easier to read.
Common patterns
Get the last N elements
const last3 = items.slice(-3); // last 3 elements as a new array
Or when you only care about a few specific ones:
const lastThree = [items.at(-3), items.at(-2), items.at(-1)];
Remove the last element immutably
const withoutLast = items.slice(0, -1);
Pairs nicely with items.at(-1) when you want both the last element and the rest:
const last = items.at(-1); const rest = items.slice(0, -1);
Quick sanity checks
if (items.at(-1) === undefined) { console.log("Array is empty or the last element is undefined"); }
Or, when you actually care about emptiness, be explicit:
if (items.length === 0) { console.log("Array is empty"); }
Try it yourself
Open your browser console and paste:
const items = ["a", "b", "c", "d", "e"]; console.log("Last:", items.at(-1)); console.log("Second-to-last:", items.at(-2)); console.log("First:", items.at(0));
Then compare with the old way:
console.log("Last (old way):", items[items.length - 1]);
Same result, fewer opportunities to get the math wrong.
Resources
- MDN: Array.prototype.at() - official documentation
- TC39 Proposal: Relative Indexing Method - the spec that introduced
at() - Can I Use: Array.prototype.at() - browser support table
Copy. Paste. Access. Your array indexing just got a little less noisy.