String.prototype.at()
Returns the character at an index, counting back from the end when the index is negative.
at(index)- Added in JavaScript ECMAScript 2022
Parameters
| Parameter | Type | Description |
|---|---|---|
$index | number | The index (position) of the character to return. A negative index counts back from the end of the string. |
Return value
string | undefinedA string containing the single UTF-16 code unit at the given position, or undefined when the index is out of range.
at() is the modern index accessor. Its whole reason to exist is the negative index: str.at(-1) is the last character, where bracket notation forces you to write str[str.length - 1] and repeat the expression that produced the string.
How it differs from the alternatives
| Out of range | Result |
|---|---|
"abc".at(5) | undefined |
"abc"[5] | undefined |
"abc".charAt(5) | "" |
That difference matters in conditionals. charAt() hands back an empty string, which is falsy but still a string; at() hands back undefined, so ?. and ?? behave the way you expect and a typo'd index shows up as undefined rather than silently becoming "".
Index handling
The index is converted to an integer, so a fractional index truncates and undefined becomes 0. Negative indexes resolve as index + str.length; if that is still negative, the result is undefined.
Like every other index-based string method, it works in UTF-16 code units, so at(-1) on a string ending in an emoji gives you the lone trailing surrogate — half a character.
Example
When to use it
- Grab the last character to check for a trailing slash or punctuation
- Read the second-to-last character without recomputing the length
- Pull an initial from a name inside a map callback
- Test the sign character at the front of a formatted number
Frequently asked questions
How do I get the last character of a string in JavaScript?
Use str.at(-1). It counts back from the end, so you never have to write str[str.length - 1], and it returns undefined for an empty string.
What is the difference between at() and charAt()?
at() accepts negative indexes and returns undefined when out of range; charAt() rejects negative indexes as out of range and returns an empty string instead of undefined.
Does String.prototype.at() work in older browsers?
It has been available across browsers since March 2022 (ECMAScript 2022). For older engines, use str[str.length - 1] or a polyfill.
More examples
at() vs charAt() vs bracket access out of range
Both report "(none)" here, but the typeof line shows the real difference: charAt() always returns a string, so a bad index is indistinguishable from a genuinely empty result.
const s = "abc";
console.log(s.at(5), s[5], JSON.stringify(s.charAt(5)));
// undefined undefined ""
// undefined lets optional chaining and ?? work
console.log(s.at(5)?.toUpperCase() ?? "(none)"); // "(none)"
// charAt gives an empty string, which is still a string
console.log(s.charAt(5).toUpperCase() || "(none)"); // "(none)"
console.log(typeof s.charAt(5), typeof s.at(5)); // string undefinedSurrogate pairs break negative indexing
at(-1) returns the trailing half of the emoji's surrogate pair. Spreading into an array first splits by code point, so Array.prototype.at() returns the whole emoji.
const msg = "Nice \u{1F44D}";
console.log(msg.at(-1).codePointAt(0).toString(16)); // dc4d — lone surrogate
console.log(msg.at(-1) === "\u{1F44D}"); // false
const chars = [...msg];
console.log(chars.at(-1) === "\u{1F44D}"); // true
console.log(chars.at(-1).codePointAt(0).toString(16)); // 1f44d
Discussion