JavaScript字符串用UTF-16编码,BMP字符占2字节,超BMP字符(如emoji)用代理对占4字节;codePointAt(index)正确返回完整码点,charCodeAt仅返回单个码元值。

JavaScript 字符串内部使用 UTF-16 编码,每个字符以 16 位码元(code unit)为单位存储。但 UTF-16 并非所有字符都占 2 字节——它用单个码元表示基本多语言平面(BMP,U+0000–U+FFFF)的字符;对超出 BMP 的字符(如某些 emoji、古汉字、数学符号),则用两个连续的 16 位码元组成“代理对”(surrogate pair)来表示,实际占用 4 字节。
charPointAt 不是标准方法,正确写法是 codePointAt
JS 中不存在 charPointAt 方法,应为 codePointAt(注意拼写)。它是 ES6 引入的,专门用于正确处理 UTF-16 中的代理对:
-
codePointAt(index)接收的是字符串中码元(16 位单元)的索引,但返回的是该位置起始的完整 Unicode 码点(code point),即一个逻辑字符的唯一编号 - 对 BMP 字符(如
'A'、'中'),codePointAt(i)和charCodeAt(i)返回值相同 - 对代理对字符(如
'?'、'?'),codePointAt(i)在高位代理位置(如索引 0)就能返回完整码点(如128522);而charCodeAt(i)只返回高位代理值(如55357),需手动组合才得真码点
为什么 charCodeAt 无法可靠获取码点
charCodeAt 操作的是单个码元,不是逻辑字符:
- 字符串
'?'实际存储为两个码元:\uD83D\uDE0A -
'?'.charCodeAt(0)→55357(高位代理) -
'?'.charCodeAt(1)→56842(低位代理) - 二者单独都不是有效字符,也非真实码点;真实码点是
128522,需按规则计算:(55357 - 0xD800) * 0x400 + (56842 - 0xDC00) + 0x10000
codePointAt 的使用要点
使用 codePointAt 时需注意索引含义:
- 它仍以码元索引为输入,不是“字符索引”。例如
'a?b'长度为 4(含两个代理码元),?占据索引 1 和 2 -
'a?b'.codePointAt(1)返回128522(? 的码点) -
'a?b'.codePointAt(2)返回98('b'的码点),因为索引 2 指向代理对的第二码元,但codePointAt会自动跳过已读取的低位代理,避免重复解析 - 推荐配合
for...of循环遍历:它按逻辑字符迭代,内部已处理代理对,更安全直观
配套方法:fromCodePoint 与 normalize
codePointAt 的反向操作是 String.fromCodePoint():
-
String.fromCodePoint(128522)→'?'(支持任意码点,包括超出 BMP 的) - 对比
String.fromCharCode(128522)→ 错误结果(只接受 0–65535,会截断) -
normalize()可处理 Unicode 等价形式(如带重音符号的字母可能有合成/分解两种表示),在比较或搜索前建议调用,避免因编码形式不同导致匹配失败
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











