Per ECMA-262, the String type is an ordered sequence of 16-bit unsigned integer elements, and operations that interpret the text treat each element as a UTF-16 code unit. length is the number of elements, not the number of characters a reader sees.
Unicode code points run from U+0000 to U+10FFFF. Code points up to U+FFFF fit in one code unit. Larger ones, such as the emoji U+1F600, are split into two code units: a leading (high) surrogate in 0xD800β0xDBFF and a trailing (low) surrogate in 0xDC00β0xDFFF. Together they form a surrogate pair. So "π".length is 2: its units are 0xD83D and 0xDE00.
What counts units and what counts code points:
- Units:
length,s[i],at,charAt,charCodeAt,slice,substring,split(""),indexOfpositions. - Code points: the string iterator (
for...of, spread,Array.from) andcodePointAt. Build one withString.fromCodePointor"\u{1F600}".
Two consequences follow. Slicing or indexing can cut a pair in half and leave a lone surrogate. And a code point is still not always what a user calls a character: "Γ©" written as e plus a combining accent is two code points. That is the next question.
Strings are immutable: s[0] = "x" throws a TypeError in strict mode (modules) and is silently ignored in sloppy mode.