String.prototype.charCodeAt()
The original character-code method, and a 16-bit one. For anything outside the Basic Multilingual Plane it returns half a character, which is why codePointAt was added.
Demo
Capital A is 65 and lowercase a is 97, a difference of 32 — the bit that old case-conversion tricks flipped. The digit 0 is 48, which is why subtracting 48 converts a digit character to its value. The out-of-range case returns NaN, not undefined and not an error, and NaN is not equal to itself — so a check like charCodeAt(i) === NaN is always false. Use Number.isNaN, or use codePointAt, which returns undefined instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| index | number | no (0) | Position of the code unit. Negative or beyond the end gives NaN rather than throwing. It does NOT count from the end. |
Return value
number — An integer from 0 to 65535 — the UTF-16 code unit at that index. NaN if the index is out of range, which is the trap.
Common patterns
const value = ch.charCodeAt(0) - '0'.charCodeAt(0);
let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
const cp = s.codePointAt(0);
Examples
Pitfalls
'abc'.charCodeAt(99) === NaN
Number.isNaN('abc'.charCodeAt(99))
'\u{1F600}'.charCodeAt(0)
'\u{1F600}'.codePointAt(0)
'abc'.charCodeAt(-1)
'abc'.at(-1).charCodeAt(0)
String.fromCharCode('É'.charCodeAt(0) + 32)
'É'.toLowerCase()
When to use
- Hashing or checksums, where a 16-bit unit is all you need
- ASCII arithmetic — digit values, simple ranges
- Interoperating with APIs that work in UTF-16 code units
- Text that may contain emoji or other astral characters → codePointAt
- You want the character, not its number → at or charAt
- Case conversion → toLowerCase and toUpperCase
- Comparing strings → localeCompare
Notes
FAQ
codePointAt for anything involving real text — it handles surrogate pairs and returns undefined rather than NaN when out of range. charCodeAt when you genuinely want UTF-16 code units, which is mostly hashing and low-level encoding work.
'\u{1F600}'.charCodeAt(0); // 55357 — half '\u{1F600}'.codePointAt(0); // 128512 — whole