js字符length问题

JavaScript 字符

基本多语言平面(BMP)

JavaScript 字符串由 16 位码元(code unit)组成。对多数字符来说,每 16 位码元对应一个字符。换句话说,字符串的 length 属性表示字符串包含多少 16 位码元:

1
2
let message = "abcde";
console.log(message.length); // 5

JavaScript 字符串使用了两种 Unicode 编码混合的策略:UCS-2UTF-16。对于可以采用 16 位编码的字符(U+0000~U+FFFF),这两种编码实际上是一样的。

对于 U+0000~U+FFFF 范围内的字符,lengthcharAt()charCodeAt()fromCharCode() 返回的结果都跟预期是一样的。这是因为在这个范围内,每个字符都是用 16 位表示的,而这几个方法也都基于 16 位码元完成操作。只要字符编码大小与码元大小一一对应,这些方法就能如期工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const messageList = ["a", "b", "c", "d"];
const message = messageList.join("");
const charAtList = [];
const charCodeList = [];

for (let i = 0; i < message.length; i++) {
charAtList.push(message.charAt(i));
}

for (let i = 0; i < message.length; i++) {
charCodeList.push(message.charCodeAt(i));
}

console.log("message length:", message.length);
console.log("charAt:", charAtList);
console.log("charCodeAt:", charCodeList);

代理对

这个对应关系在扩展到 Unicode 增补字符平面时就不成立了。问题很简单,即 16 位只能唯一表示65536 个字符。这对于大多数语言字符集是足够了,在 Unicode 中称为基本多语言平面(BMP)。为了表示更多的字符,Unicode 采用了一个策略,即每个字符使用另外 16 位去选择一个增补平面。这种每个字符使用两个 16 位码元的策略称为代理对

在涉及增补平面的字符时,前面讨论的字符串方法就会出问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const messageList = ["a", "b", "👨‍👩‍👧‍👦", "c", "d"];
const message = messageList.join("");
const charAtList = [];
const charCodeList = [];

for (let i = 0; i < message.length; i++) {
charAtList.push(message.charAt(i));
}

for (let i = 0; i < message.length; i++) {
charCodeList.push(message.charCodeAt(i));
}

console.log(message.length);
console.log("charAt", charAtList);
console.log("charCodeAt", charCodeList);

这些方法仍然将 16 位码元当作一个字符,事实上索引 2 至 索引 12 对应的码元应该被看成一个代理对,只对应一个字符。

解决方法

Intl.Segmenter

Intl.Segmenter 对象支持语言敏感的文本分割,允许你将一个字符串分割成有意义的片段(字、词、句)。

1
2
3
4
5
const message = "你好,ab👨‍👩‍👧‍👦cd";

const segmenterFr = new Intl.Segmenter("zh-CN", { granularity: "grapheme" });

const length = Array.from(segmenterFr.segment(message)).length;

为了方便使用,可以给字符串扩展一个名为 segmenterLength 的方法,用于计算字符串中包含的字符数量。

1
2
3
String.prototype.segmenterLength = function () {
return Array.from(new Intl.Segmenter("zh-CN", { granularity: "grapheme" }).segment(this)).length;
};

参考文献

  • MDN
  • JavaScript 高级程序设计 第 4 版

js字符length问题
https://www.my-web.cn/js/code-unit/
发布于
2026年5月26日
许可协议