String.prototype.at()

Added so that reading the last character stops requiring arithmetic. Bracket notation cannot take a negative index, and charAt will not either.

String methodES2022Live demo
Common call
s.at(-1)
Returns
one character, or undefined
Replaces
s[s.length - 1] and s.charAt(s.length - 1)
Watch out
out of range is undefined, while charAt gives ""
string.at(indexindex — Position to read. Negative counts back from the end, so -1 is the last character. Out of range gives undefined rather than throwing.type: number · required)
→ string | undefined

Demo

Live evaluation
Try:
Inputs
sstringthe source string
indexnumberindex (may be negative)
Output
'abcdef'.at(0)
'a'

Negative indices are the entire reason this method exists: at(-1) is the last character, with no reference to length anywhere. The out-of-range case returns undefined, which is the meaningful difference from charAt — charAt(99) returns an empty string, so a truthiness check treats a missing character and a real one very differently depending on which method you used. Neither one throws.

Parameters

NameTypeRequiredDescription
indexnumberyesPosition to read. Negative counts back from the end, so -1 is the last character. Out of range gives undefined rather than throwing.

Return value

string | undefined — A one-character string, or undefined when the index is out of range. charAt returns an empty string in that case instead.

Common patterns

Read the last character
The reason the method was added.
const last = s.at(-1);
Test a trailing character
Clearer than a length calculation.
if (path.at(-1) !== '/') path += '/';
Capitalise the first letter
at reads it; slice takes the rest.
const title = s.at(0).toUpperCase() + s.slice(1);

Examples

1. First character
'abcdef'.at(0)
Returns
'a'
2. Last character
'abcdef'.at(-1)
Returns
'f'
3. Out of range
'abcdef'.at(99)
Returns
undefined
4. charAt differs
'abcdef'.charAt(99)
Returns
''
5. Brackets differ
'abcdef'[99]
Returns
undefined
6. Negative brackets
'abcdef'[-1]
Returns
undefined

Pitfalls

1. Bracket notation does not accept negatives
s[-1] is not an error and not the last character — it looks up a property literally named "-1", which does not exist, so you get undefined. This is why the method was added at all.
Property lookup
'abc'[-1]
undefined
Use at
'abc'.at(-1)
'c'
2. at and charAt disagree when out of range
at gives undefined, charAt gives an empty string. Both are falsy, so a simple if behaves the same — but concatenation does not, and "undefined" appearing in output is usually traced back to exactly this.
Stringifies badly
'x' + 'abc'.at(99)
'xundefined'
charAt, or default
'x' + ('abc'.at(99) ?? '')
'x'
3. It reads a code unit, not a character
Like every index-based string operation, at works in UTF-16. The first "character" of a string starting with an emoji is half a surrogate pair, which renders as a replacement glyph.
Half an emoji
'\u{1F600}a'.at(0)
'\ud83d'
Spread first
[...'\u{1F600}a'].at(0)
'\u{1F600}'
4. ES2022 — check your runtime
Node 16.6+ and 2021-era browsers. Older targets need the length arithmetic, or slice(-1) which has worked since ES3 and returns an empty string rather than undefined.
Missing
s.at(-1)
TypeError: s.at is not a function
slice(-1)
s.slice(-1)
last character, or ""

When to use

Use it
  • Reading the last character, or the nth from the end
  • Any single-character read where the index may be negative
  • Replacing s[s.length - 1] for readability
Reach for something else
  • You want a substring, not one character → slice
  • You need an empty string rather than undefined → charAt
  • The text contains emoji → spread into an array of code points
  • Targeting runtimes older than 2021 → slice(-1)

Notes

Complexity
O(1)
Return
A new one-character string, or undefined
CPython impl
V8: Builtins-string-at
Memory
Allocates a one-character string
Thread-safe
Single-threaded; the string is only read

FAQ

at for anything involving the end of the string, since it is the only one that takes a negative index. Brackets are fine and fastest for a known non-negative index. charAt is worth knowing mainly because it returns an empty string rather than undefined when out of range.

s.at(-1);      // last character
s[0];          // first, terse
s.charAt(99);  // '' rather than undefined

History

ES1
charAt present from the first version of the language.
ES2022
at added to String, Array and the typed arrays together, bringing negative indexing to all of them.