charCodeAt 方法不支持四字节 Unicode 码点,仅返回 UTF-16 编码单元值;对辅助平面字符(如 emoji、古汉字)会拆分为代理对,导致 length 计数和遍历异常。

JavaScript 的 charCodeAt 方法本身不完全兼容 Unicode 全集,尤其对四字节码点(U+10000 及以上)——比如大部分 emoji、古汉字、数学符号等——它只能返回 UTF-16 编码单元,而非真实码点。这是由 JavaScript 字符串底层采用 UTF-16 编码决定的,不是 bug,而是设计限制。
charCodeAt 只读取单个 UTF-16 编码单元
JavaScript 字符串中每个“位置”对应一个 16 位编码单元(code unit),charCodeAt(i) 就是取这个位置上的数值:
- 对基础平面字符(U+0000–U+FFFF),如
"A"、"中",charCodeAt返回的就是其真实 Unicode 码点; - 对辅助平面字符(U+10000 起),如
"?"(U+20BB7)、"?"(U+1F30D),它们在内存中占两个连续位置(代理对),charCodeAt(0)和charCodeAt(1)分别返回高位代理和低位代理值(如0xD842和0xDFB7),单独看毫无语义,也无法还原原码点。
为什么 length 和遍历会出错
因为 str.length 统计的是 UTF-16 编码单元个数,不是字符个数:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
"?".length === 2,但实际只有 1 个字符; -
"?".charAt(0)返回空字符或乱码(取决于环境),.charAt(1)才是另一半; - 用
for (let i = 0; i 遍历时,会把一个 emoji 当成两个“字符”处理,容易切断代理对,导致显示异常或数据损坏。
正确处理四字节字符的替代方案
要真正按 Unicode 字符(而非 UTF-16 单元)操作,应使用现代 API:
立即学习“Java免费学习笔记(深入)”;
-
str.codePointAt(i):从索引i开始识别代理对,返回完整码点(如"?".codePointAt(0) === 0x20BB7); -
String.fromCodePoint(code):可安全生成任意码点字符,包括辅助平面; - 扩展运算符或
Array.from(str):能正确拆分字符串为字符数组(自动处理代理对),例如[..."?"]→["?"],而不是["", ""]; - 正则搭配
/u标志:如str.match(/./gu)可逐字符匹配,避免代理对断裂。
兼容旧环境的 fallback 写法
若需支持老旧浏览器(如 IE),可用手动判断代理对的方式模拟 codePointAt:
- 检查
charCodeAt(i)是否落在高位代理范围(0xD800–0xDBFF); - 若是,再取
i+1位置的低位代理(0xDC00–0xDFFF),组合计算:(hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000; - 否则直接返回
charCodeAt(i)。

















