The String codePointAt() method

By

Learn how the JavaScript codePointAt() method returns the full Unicode code point of a character, handling symbols that need two 16-bit units in one call.

~~~

codePointAt() returns the full Unicode code point of the character at a given position in a string. It was introduced in ES2015 to handle characters that cannot be represented by a single 16-bit unit, but need 2 instead.

JavaScript strings store text as a sequence of 16-bit units, the UTF-16 encoding. Most characters fit in one unit. But many don’t: emoji, and lots of CJK ideographs, take two units, called a surrogate pair.

The older charCodeAt() method only sees one 16-bit unit at a time. For a character made of two units, you need to retrieve the first, then the second, and combine them. codePointAt() gives you the whole character in one call.

An example

This chinese character ”𠮷” is composed of 2 UTF-16 units:

'𠮷'.charCodeAt(0).toString(16) //'d842'
'𠮷'.charCodeAt(1).toString(16) //'dfb7'

Neither of those values is a real character on its own. They only mean something as a pair:

'\ud842\udfb7' //'𠮷'

With codePointAt() you get the actual code point directly:

'𠮷'.codePointAt(0) //134071
'𠮷'.codePointAt(0).toString(16) //'20bb7'

The method returns a number, 134071 in this case. Converting it to hexadecimal gives 20bb7, and you can use that in a Unicode escape sequence to write the character:

'\u{20bb7}' //'𠮷'

There’s a matching static method to go the other way, from code point to string:

String.fromCodePoint(134071) //'𠮷'

A pitfall with positions

The index you pass to codePointAt() still counts 16-bit units, not characters. Our single visible character has a length of 2:

'𠮷'.length //2

So codePointAt(1) doesn’t return the next character. It returns the second half of the surrogate pair, 57271, which is not a valid character by itself.

If you need to walk a string character by character, don’t loop over indexes. Spread the string into an array instead, because the iterator is code-point aware:

[...'𠮷 ok'].map((c) => c.codePointAt(0))
//[ 134071, 32, 111, 107 ]

Each element here is a full character, so codePointAt(0) on each one always gives the right answer.

More on Unicode and working with it in Unicode and UTF-8.

~~~

Related posts about js: