charAt()和charCodeAt方法以及Unicode

在 JavaScript 中,字符串内部是以 UTF-16 编码存储和处理的。

UTF-16 是一种可变长度的 Unicode 编码方式,它使用 16 位(2 字节)或 32 位(4 字节) 来表示 Unicode 字符

对于基本多语言平面(BMP)内的字符(U+0000 到 U+FFFF),可以直接用 一个 16 位单元(称为 code unit) 表示
例如:

  • 'A'(U+0041) → 0x0041
  • '中'(U+4E2D) → 0x4E2D
'A'.length;        // 1
'中'.length;       // 1

对于辅助平面(Supplementary Planes)中的字符(U+10000 到 U+10FFFF),无法用单个 16 位单元表示,因此 UTF-16 使用 代理对(surrogate pair):

  • 一个高位代理(high surrogate):范围 0xD8000xDBFF(BMP中的特殊码点)
  • 一个低位代理(low surrogate):范围 0xDC000xDFFF(BMP中的特殊码点)

举例:
码点:U+1F600(😀,笑脸 emoji)的 UTF-16 编码为:0xD83D 0xDE00

'😀'.length;       // 2 (U+1F600)
'🚀'.length;       // 2 (U+1F680)

所以,注意str[index] 或 str.charAt(index) 返回的是 UTF-16 码元,不一定是完整 Unicode 字符,它可能返回单独代理项(lone surrogate)。

同样,String 的 charCodeAt(index) 方法返回一个整数,表示给定索引处的 UTF-16 码元,其值介于 0 和 65535 之间,此整数代表的码元,也不一定是完整 Unicode 字符。

如果要获取给定索引处的完整 Unicode 码位,请使用 String.prototype.codePointAt() 方法。

<script type="text/javascript">

let str="Hello world!";
console.log(str.charAt(4)); //输出字符 o
console.log(str.charCodeAt(4));//输出111(10进制)

let s = '😀';
console.log(s[0]); //  (高位代理,单独无意义)
console.log(s[1]); //  (低位代理,单独无意义)

</script>

正确处理完整 Unicode 字符的方法

  • 使用 for...of(基于码点迭代):
    for (let char of '😀') {
      console.log(char); // '😀'
    }
    
  • 使用 Array.from(str)
    Array.from('😀').length; // 1
    
  • 使用 String.fromCodePoint()codePointAt()
    '😀'.codePointAt(0); // 128512 (0x1F600)
    String.fromCodePoint(0x1F600); // '😀'
    

总结:

✅ JavaScript 字符串在内部使用 UTF-16 编码
⚠️ 对于超出 BMP 的字符(U+10000 及以上),它们由两个 16 位单元(代理对)表示,因此:

  • .length 可能不等于“视觉字符数”
  • 逐索引访问可能得到无意义的代理项

在处理国际化文本、emoji 或用户输入时,应优先使用 码点(code point) 相关 API(如 codePointAt, fromCodePoint, for...of)来避免错误。

posted @ 2016-10-09 12:15  悠哉大斌  阅读(6797)  评论(0)    收藏  举报