迹忆客 专注技术分享

当前位置:主页 > 学无止境 > WEB前端 > JavaScript >

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

作者:迹忆客 最近更新:2023/01/08 浏览次数:

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

让我们从基础开始……

臭名昭著的BMP

Unicode 通过明确的名称和称为其代码点的整数来识别字符。 例如,© 字符被命名为“版权标志”,其代码点为 U+00A9——0xA9 可以写成十进制的 169

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

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

其他十六个位面 (U+010000 → U+10FFFF) 称为补充位面或星体位面。 我不会在这里讨论它们; 请记住,有 BMP 字符和非 BMP 字符,后者也称为增补字符或星体字符。


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

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

UCS-2(2-byte Universal Character Set)通过简单地使用代码点作为 16 位代码单元来产生固定长度的格式。 对于 0 到 0xFFFF 范围内的大多数代码点(即 BMP),这会产生与 UTF-16 完全相同的结果。

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

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

代理对

BMP 之外的字符,例如 U+1D306 tetragram for center (𝌆),只能使用两个 16 位代码单元以 UTF-16 编码:0xD834 0xDF06。 这称为代理对。 请注意,代理对仅表示单个字符。

代理对的第一个代码单元始终在 0xD8000xDBFF 的范围内,称为高代理或前导代理。

代理对的第二个代码单元总是在 0xDC000xDFFF 的范围内,称为低代理或尾代理。

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

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

Unicode 标准 3.0 的第 3.7 节定义了在代理对之间进行转换的算法。

根据以下公式,大于 0xFFFF 的代码点 C 对应于代理项对 <H, L>

H = Math.floor((C - 0x10000) / 0x400) + 0xD800
L = (C - 0x10000) % 0x400 + 0xDC00

反向映射,即从代理项对 <H, L> 到 Unicode 代码点 C,由下式给出:

C = (H - 0xD800) * 0x400 + L - 0xDC00 + 0x10000

好的,那么 JavaScript 呢?

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

符合本国际标准的实施应解释符合 Unicode 标准 3.0 版或更高版本和 ISO/IEC 10646-1 的字符,采用 UCS-2UTF-16 作为采用的编码形式,实施级别 3。如果采用 ISO/IEC 10646-1子集没有特别说明,假定为BMP子集,集合300。如果没有特别说明采用的编码形式,则假定为UTF-16编码形式。

换句话说,允许 JavaScript 引擎使用 UCS-2UTF-16

但是,无论引擎的内部编码如何,规范的特定部分都需要一些 UTF-16 知识。

当然,内部引擎细节对普通 JavaScript 开发人员来说并不重要。 更有趣的是 JavaScript 认为什么是“字符”,以及它如何公开这些字符:

在本文档的其余部分,短语代码单元和单词字符将用于指代用于表示单个 16 位文本单元的 16 位无符号值。 短语 Unicode 字符将用于指代由单个 Unicode 标量值(可能长于 16 位,因此可能由多个代码单元表示)表示的抽象语言或印刷单位。 短语代码点指的是这样一个 Unicode 标量值。 Unicode 字符仅指由单个 Unicode 标量值表示的实体:组合字符序列的组件仍然是单独的“Unicode 字符”,即使用户可能将整个序列视为单个字符。

JavaScript 将代码单元视为单个字符,而人类通常根据 Unicode 字符来思考。 这对 BMP 之外的 Unicode 字符有一些不幸的后果。 由于代理对由两个代码单元组成,'𝌆'.length == 2,即使那里只有一个 Unicode 字符。 各个代理项的一半被暴露,就好像它们是字符一样:'𝌆' == '\uD834\uDF06'

让你想起了什么? 它应该,因为这几乎正是 UCS-2 的工作方式。 (唯一的区别是,从技术上讲,UCS-2 不允许代理字符,而 JavaScript 字符串允许。)

我们可能会争辩说它类似于 UTF-16,除了允许不匹配的代理项一半、错误顺序的代理项是允许的,并且代理项的一半作为单独的字符公开。 我想您会同意将此行为视为“带代理的 UCS-2”更容易。

这种类似于 UCS-2 的行为会影响整个语言——例如,与支持 UTF-16 的语言相比,增补字符范围的正则表达式更难编写。

代理项对仅在浏览器显示时(在布局期间)才重新组合为单个 Unicode 字符。 这发生在 JavaScript 引擎之外。 为了演示这一点,我们可以在单独的 document.write() 调用中写出高代理项和低代理项:document.write('\uD834'); document.write('\uDF06');。 这最终被渲染为𝌆——一个字形。


总结

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

然而,ECMAScript/JavaScript 语言本身根据 UCS-2 而非 UTF-16 公开字符。

如果我们需要转义 Unicode 字符,在必要时将其分成代理项的两半,请随时使用我的 JavaScript 转义工具。

如果你想计算 JavaScript 字符串中 Unicode 字符的数量,或者创建一个基于非 BMP Unicode 代码点的字符串,你可以使用 Punycode.js 的实用函数在 UCS-2 字符串和 UTF-16 代码点之间进行转换 :

// `String.length` replacement that only counts full Unicode characters
punycode.ucs2.decode('𝌆').length; // 1
// `String.fromCharCode` replacement that doesn’t make you enter the surrogate halves separately
punycode.ucs2.encode([0x1D306]); // '𝌆'
punycode.ucs2.encode([119558]); // '𝌆'

ECMAScript 6 将支持一种新的字符串转义序列,即 Unicode 代码点转义,例如 \u{1D306}。 此外,它将定义 String.fromCodePointString#codePointAt,它们都接受代码点而不是代码单元。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

Do you understand JavaScript closures?

发布时间:2025/02/21 浏览次数:108 分类:JavaScript

The function of a closure can be inferred from its name, suggesting that it is related to the concept of scope. A closure itself is a core concept in JavaScript, and being a core concept, it is naturally also a difficult one.

Do you know about the hidden traps in variables in JavaScript?

发布时间:2025/02/21 浏览次数:178 分类:JavaScript

Whether you're just starting to learn JavaScript or have been using it for a long time, I believe you'll encounter some traps related to JavaScript variable scope. The goal is to identify these traps before you fall into them, in order to av

How much do you know about the Prototype Chain?

发布时间:2025/02/21 浏览次数:150 分类:JavaScript

The prototype chain can be considered one of the core features of JavaScript, and certainly one of its more challenging aspects. If you've learned other object-oriented programming languages, you may find it somewhat confusing when you start

JavaScript POST

发布时间:2024/03/23 浏览次数:96 分类:JavaScript

本教程讲解如何在不使用 JavaScript 表单的情况下发送 POST 数据。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便