得鹿梦鱼 得鹿梦鱼

JavaScript的内部字符编码

ECMAScript 是 JavaScript 的标准化版本,它定义了如何解释字符

本国际标准的一致实现应按照 Unicode 标准 3.0 版或更高版本以及 ISO/IEC 10646-1 来解释字符,并以 UCS-2 或 UTF-16 作为所采用的编码形式,实现级别 3。如果所采用的 ISO/IEC 10646-1 子集未另行指定,则假定为 BMP 子集,集合 300。如果所采用的编码形式未另行指定,则假定为 UTF-16 编码形式

换句话说,JavaScript 引擎可以使用 UCS-2 或 UTF-16。

JavaScript 的内部字符编码:UCS-2 还是 UTF-16?

JavaScript 使用 UCS-2 还是 UTF-16 编码?由于我在任何地方都找不到这个问题的明确答案,所以我决定研究一下。答案取决于你指的是什么:JavaScript 引擎,还是 JavaScript 语言层面

Unicode 通过一个明确的名称和一个称为其代码点的整数来标识字符。例如,一个©字符名为“版权符号”,其代码点为 U+00A9(十进制0xA9表示为 )。169

Unicode 代码空间分为17个平面,每个平面有2^16(65,536)个代码点。其中一些代码点尚未分配字符值,一些保留作私人用途,还有一些永久保留为非字符。每个平面中的代码点具有从十六进制值xy0000的到xyFFFF,其中xy是一个从0010的十六进制值,表示这些值属于哪个平面

第一个平面(xy为00)称为基本多文种平面(BMP)。它包含从 U+0000U+FFFF 的代码点,这些代码点是最常用的字符

其余十六个平面(U+010000U+10FFFF)称为补充平面或星面,这里将字符分为BMP 字符和非 BMP 字符,后者也称为补充字符或星面

UCS-2 和 UTF-16 之间的区别

UCS-2 和 UTF-16 都是 Unicode 的字符编码

UCS-2(2字节通用字符集)通过简单地将代码点用作16位代码单元来生成固定长度的格式。对于从到范围内的大多数代码点(即BMP),这会产生与UTF-16完全相同的结果。从00xFFFF

UTF-16(16 位 Unicode 转换格式)是 UCS-2 的扩展,允许表示 BMP 之外的代码点。它为每个代码点生成一个或两个 16 位代码单元的可变长度结果。这样,它可以编码从到范围内的代码点。从00x10FFFF

例如,在 UCS-2 和 UTF-16 中,BMP 字符U+00A9 版权符号 © 均被编码为0x00A9

代码点

Unicode为每个字符分配的唯一数字,范围:0x0000 ~ 0x10FFFF

代理对

BMP 之外的字符,例如中心的 U+1D306 四字母组(𝌆),只能使用两个 16 位代码单元进行 UTF-16 编码:0xD834 0xDF06。这称为代理对。请注意,代理对仅代表单个字符

代理对的第一个代码单元始终在从0xD8000xDBFF的范围内,并且被称为高代理或前导代理
代理对的第二个代码单元始终在从0xDC000xDFFF的范围内,并且被称为低代理或尾随代理

UCS-2 缺乏代理对的概念,因此将0xD834 0xDF06(以前的 UTF-16 编码)解释为两个单独的字符

代码点和代理对之间的转换

代码点到代理对

仅当代码点 > 0xFFFF 时需要转换

计算偏移量:offset = codePoint - 0x10000
高代理项:high = 0xD800 + offset >> 10 (右移10位等价于除以 0x400,即 1024)
低代理项:low = 0xDC00 + offset & 0x3FF (取低10位)

代理对到代码点

高代理项和低代理项必须有效。

验证范围:
0xD800 ≤ high ≤ 0xDBFF
0xDC00 ≤ low ≤ 0xDFFF
计算代码点:
codePoint = 0x10000 + high - 0xD800 << 10 + low - 0xDC00

JavaScript 引擎可以自由选择在内部使用 UCS-2 或 UTF-16。据我所知,大多数引擎都使用 UTF-16,但无论它们做出何种选择,这都只是一个实现细节,不会影响语言的特性。

然而,ECMAScript/JavaScript 语言本身根据 UCS-2 而不是 UTF-16 显示字符