EmoArt
发布
术语表

UTF-16 - JavaScript 字符串背后的编码

最近更新: 2026-05-23·约 4 分钟

本文约 4 分钟读完。

如果你的 JavaScript 代码显示 "🌸".length 为 2,你就已经遇到了 UTF-16。这种编码在 emoji 出现之前一直是隐形的。UTF-16 是一种 Unicode 编码,大多数字符用 2 字节表示,补充字符 (包括大多数 emoji) 通过代理对用 4 字节表示。 它是 JavaScript、Java、C# 和 Windows 部分组件的内部字符串表示。理解它的特性对于正确处理 emoji 至关重要。

定义

UTF-16 (16-bit Unicode Transformation Format) 将 Unicode 码位编码为 16 位码元的序列。 基本多语言平面 (BMP, U+0000 到 U+FFFF) 中的码位占用一个 16 位码元 (2 字节)。 U+FFFF 以上的码位以代理对编码 - 两个 16 位码元合计 4 字节。

两层结构

码位范围码元数字节数示例
U+0000 - U+FFFF (BMP)12ASCII、拉丁、CJK 字符
U+10000 - U+10FFFF2 (代理对)4大多数 emoji、古文字

代理对

对于 U+FFFF 以上的码位,UTF-16 将其值拆分为两个 16 位码元:

  • 高位代理: U+D800 到 U+DBFF (1024 个可能值)
  • 低位代理: U+DC00 到 U+DFFF (1024 个可能值)

两者组合可产生 1024 × 1024 = 1,048,576 个码位,恰好覆盖补充平面 (U+10000 到 U+10FFFF)。 代理值本身是保留的,在有效的 Unicode 文本中不会作为独立字符出现。

示例: 🌸 (U+1F338)

樱花 emoji 的码位 U+1F338 编码为:

  • 高位代理: 0xD83C
  • 低位代理: 0xDF38
  • UTF-16 字节: D8 3C DF 38 (共 4 字节)

在 JavaScript 字符串中,这存储为两个码元。"🌸".length 返回 2,因为 length 计算的是码元数而非码位数。

UTF-16 的使用场景

  • JavaScript: String 内部使用; lengthcharAtcharCodeAt 操作的是码元
  • Java: Stringchar 是 UTF-16
  • C# / .NET: string 是 UTF-16
  • Windows API: 许多 API 使用 UTF-16 处理字符串
  • Qt、Cocoa、ICU: 许多字符串操作内部使用 UTF-16

相比之下,UTF-8 是主流的传输格式 (HTTP、JSON、文件)。许多系统使用 UTF-8 进行存储和传输, 使用 UTF-16 进行内存中的处理。

JavaScript 中的陷阱

length 与可见字符数

"a".length              // 1
"あ".length             // 1 (BMP)
"🌸".length             // 2 (代理对)
"👨‍👩‍👧".length            // 8 (3 个 emoji + 2 个 ZWJ 的代理对)
[..."🌸"].length        // 1 (码位迭代)
[..."👨‍👩‍👧"].length       // 5 (码位,非字素)

字符串反转会破坏 emoji

"🌸".split("").reverse().join("")
// 错误: 代理对被分离,显示为 ?? 或无效字符

[..."🌸"].reverse().join("")
// 正确: 码位感知的反转

// 更好的方式: 使用 Intl.Segmenter 处理字素簇

charCodeAt 返回代理码元

"🌸".charCodeAt(0) 返回 55356 (0xD83C,高位代理),而非码位 127800。 使用 codePointAt(0) 可以获取实际码位,它会合并代理对。

UTF-8 与 UTF-16 的权衡

方面UTF-8UTF-16
ASCII 大小1 字节2 字节
CJK 大小3 字节2 字节
emoji 大小4 字节4 字节
自同步是 (通过代理范围)
字节序存在 BE/LE 变体

常见误解

  • ❌「UTF-16 每个字符总是 2 字节」→ ✅ 补充字符通过代理对使用 4 字节
  • ❌「str.length 返回字符数」→ ✅ 它返回的是 UTF-16 码元数
  • ❌「UTF-16 正在被淘汰」→ ✅ 它深深嵌入在 JavaScript、Java、.NET 和 Windows API 中,短期内不会消失

相关术语

  • Unicode - UTF-16 所编码的字符标准
  • ZWJ - 跨越多个代理对的序列

这篇文章对你有帮助吗?