charcodeat 返回 utf-16 编码值,对超出 bmp 的字符(如 emoji)会拆解为代理对的高位或低位码点;代理对由两个 16 位码元组成,需组合还原为完整 unicode 码点。

charCodeAt 返回字符串中指定位置字符的 UTF-16 编码值(0–65535),对 ASCII 字符表现直观,但遇到 emoji、中文、带变音符号的字母等**超出基本多文种平面(BMP)的字符时,会返回代理对(surrogate pair)中的高位或低位码点,而非完整 Unicode 码点**。
什么是代理对?为什么 charCodeAt 会“拆开”一个字符?
Unicode 中,U+10000 及以上的字符(如 ?、??、?)在 JavaScript 字符串中以两个 16 位码元(即代理对)存储:一个高位代理(0xD800–0xDFFF) + 一个低位代理(0xD800–0xDFFF)。而 charCodeAt 每次只读取一个索引位置的 16 位值,因此对这类字符会分别返回两个独立的数值,看起来像“一个字符占两个位置”。
- 例如:
"?".length === 2,"?".charCodeAt(0)返回 55348(0xD83C),"?".charCodeAt(1)返回 57216(0xDF3C) - 单独看这两个数毫无语义,组合起来才是真正的 Unicode 码点 U+1F30D(用公式
(high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000可还原)
常见误用场景与风险
直接用 charCodeAt 遍历字符串并做字符判断或截断,容易出错:
- 遍历时跳过半个代理对 → 导致乱码或
String.fromCharCode拼出无效字符 - 用
charCodeAt(i) === 65判断是否为 'A' 是安全的,但charCodeAt(i) === 0x1F30D永远不成立(因为 0x1F30D > 65535) - 计算“字符数”时用
str.length会把 emoji 当作 2 个单位,而非 1 个视觉字符
更安全的替代方案
现代 JavaScript 提供了基于 Unicode 码点的操作方式,避免代理对干扰:
- 用
str.codePointAt(i)替代charCodeAt(i):返回完整的 Unicode 码点(支持 > 0xFFFF 的值) - 用 for…of 遍历字符串:
for (const ch of "?abc") { console.log(ch); }—— 自动按 Unicode 字符(而非 UTF-16 码元)分割 - 需要兼容旧环境时,可用
Array.from(str)或正则str.match(/[\s\S]/gu)获取真正字符数组
小结:何时还能放心用 charCodeAt?
在明确只处理 ASCII(U+0000–U+007F)或 BMP 内字符(如大部分中文、拉丁扩展-A)且无需跨平台一致性的简单场景下,charCodeAt 仍够用。但只要涉及 emoji、古文字、数学符号、越南文/阿拉伯文等复杂文本,就应优先使用 codePointAt 和 for…of。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











