codePointAt 是 JavaScript 中唯一能正确获取超出 BMP 的 Unicode 码点的方法,它自动识别并合并代理对,返回完整 32 位码点;而 charCodeAt 仅返回单个 16 位码元,无法还原真实字符。

codePointAt 是 JavaScript 中正确处理超出基本多文种平面(BMP,即 Unicode 码点 U+0000–U+FFFF)字符的关键方法,比如 emoji(如 ?、??)、古文字、部分中文生僻字等。这些字符在 UTF-16 编码中以代理对(surrogate pair)形式存储,占两个 16 位单元。传统 charCodeAt 只能返回单个 16 位单元的值,无法还原真实码点;而 codePointAt 能自动识别代理对并返回完整的 Unicode 码点(32 位整数)。
为什么 codePointAt 能正确处理超出 BMP 的字符
JavaScript 字符串内部使用 UTF-16 编码。对于码点 ≥ U+10000 的字符(如 ? U+1F30D),引擎将其拆分为高代理(high surrogate)和低代理(low surrogate)两个 16 位值。调用 str.charCodeAt(0) 只会得到高代理值(如 0xD83C),毫无语义;而 str.codePointAt(0) 检测到后续位置是匹配的低代理(如 0xDF0D),就合并计算出真实码点 0x1F30D(127757)。
使用 codePointAt 的注意事项
- 参数是字符索引(不是码元索引),但返回的是该字符起始位置对应的完整码点——即使该字符占两个码元,也只需传首码元的索引(如
"?".codePointAt(0) === 127757) - 若索引指向低代理码元(如
"?".codePointAt(1)),则只返回该低代理的数值(0xDF0D),不会反向查找高代理——所以应避免对代理对中间位置调用 - 返回值是数字,需用
Number.isNaN()判断是否有效(空字符串或越界时返回undefined)
遍历字符串并提取所有码点的推荐方式
不能简单用 for (let i = 0; i 遍历,因为长度按 UTF-16 码元计,一个 emoji 占 2 个长度却只算 1 个字符。正确做法是结合 <code>codePointAt 和字符宽度跳转:
for (let i = 0; i < str.length; i++) {
const cp = str.codePointAt(i);
console.log(cp.toString(16).toUpperCase()); // 如 "1F30D"
i += cp > 0xFFFF ? 1 : 0; // 跳过下一个码元(代理对第二部分)
}
更简洁的方式是使用 for...of 循环(ES2015+),它天然按 Unicode 字符(而非码元)迭代:
立即学习“Java免费学习笔记(深入)”;
for (const char of str) {
console.log(char.codePointAt(0).toString(16));
}
与其它 Unicode 处理方法的配合
codePointAt 常与 String.fromCodePoint 成对使用:前者解码,后者编码。例如还原一个 emoji:
"?".codePointAt(0) → 127757String.fromCodePoint(127757) → "?"
注意:String.fromCharCode 仅支持 16 位,无法生成超出 BMP 的字符;必须用 fromCodePoint。


















