JavaScript 的 length 属性返回 UTF-16 编码单元数而非 Unicode 字符数,故含代理对(如 emoji、增补汉字)时会高估;应使用 [...str].length 或 Array.from(str).length 获取真实字符数。

JavaScript 的 length 属性返回字符串的 UTF-16 编码单元(code unit)个数,不是 Unicode 字符(code point)个数。这意味着对含多字节 Unicode 字符(如 emoji、中文、某些生僻字)的字符串,length 可能与“人眼感知的字符数”不一致。
为什么 length 会“算错”中文和 emoji?
JavaScript 字符串内部使用 UTF-16 编码。大部分常用字符(如 ASCII 字母、常见汉字)用 1 个 UTF-16 单元表示(占 2 字节),length 值准确。但部分字符(如 U+1F600 ?、U+20000 ? 等)超出基本多文种平面(BMP),需用两个 UTF-16 单元(即代理对,surrogate pair)表示,此时 length 返回 2,而实际只对应 1 个 Unicode 字符。
-
"a".length→ 1(ASCII,单单元) -
"你好".length→ 2(常用汉字,各占 1 单元) -
"??".length→ 5(带 ZWJ 的组合 emoji,含多个代理对和连接符) -
"?".length→ 2(U+2070E,位于增补平面,需代理对)
如何获取真正的 Unicode 字符数?
用扩展运算符([...str])或 Array.from(str) 将字符串按 Unicode 字符(code point)拆分为数组,再取 length:
-
[..."??"].length→ 1 -
Array.from("?").length→ 1 -
Array.from("a?中??").length→ 4(每个都是独立 Unicode 字符)
注意:此方法能正确处理代理对、组合字符(如带变音符号的字母)、ZJW 连接符等现代 Unicode 特性。
立即学习“Java免费学习笔记(深入)”;
截取、遍历字符串时的常见陷阱
直接用 str.substring(0, n) 或 for (let i = 0; i 可能切开代理对,导致乱码或异常字符:
-
"?".substring(0, 1)→""(高代理单元孤立,显示为替换符) - 错误遍历可能跳过字符或重复解析
安全做法:
- 截取用
Array.from(str).slice(0, n).join("") - 遍历用
for (const char of str)(原生支持 code point 遍历) - 或使用
String.prototype.codePointAt()+String.fromCodePoint()手动处理
兼容旧环境的备选方案
若需支持不支持扩展运算符或 for...of 的老旧引擎(如 IE),可用正则匹配 Unicode 字符:
function trueLength(str) {
return str.match(/[\s\S]/u)?.length || 0;
}
// 注意:需添加 u 标志启用 Unicode 模式
或使用第三方库如 grapheme-splitter 处理更复杂的字形簇(如带肤色修饰符的 emoji)。









