前端工程师需要了解的 Unicode 知识
str.length <= 20
"😂".length
String.prototype.length
是如何定义的?The String type is the set of all ordered sequences of zero or more 16-bit unsigned integer values (“elements”) up to a maximum length of 253 - 1 elements.
The number of elements in the String value represented by this String object.
length
属性表示 16bit 单元的个数
为什么有些字符占用 16bit
而有些字符占用 32bit
UTF-16 编码与代理对
Unicode 目前共定义了 0x10FFFF 个码点。其中 0x0000 - 0xFFFF 收录了各国语言中最常用的文字和符号,称为基本多语种平面。
0x010000 - 0x10FFFF 称为扩展平面,用来收录使用频率较低的字符。
Unicode字符平面映射UTF-16 是一种变长编码。对于基本多语种平面内的码点使用 16 位表示,而对于扩展平面内的码点则需要通过代理对技术使用 32 位来表示。
代理对技术是使用基本多文种平面中预留的 2048 个码位(0xD800 - 0xDFFF),通过 2 个 16bit 位置来表示一个位于扩展平面中字符的技术。
转换规则
// 码点转代理对
高位 = Math.floor((码点 - 0x010000) / 1024) + 0xD800
低位 = (码点 - 0x010000) % 1024 + 0xDC00
// 代理对转码点
码点 = (高位 - 0xD800) * 1024 + (低位 - 0xDC00) + 0x010000
JavaScript 中获得字符串的 Unicode 字符数
function countUnicodeCharacters (str) {
let result = 0;
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
if (charCode < 0xDC00 || charCode > 0xDFFF) {
result ++
}
}
return result;
}
太麻烦?
ECMAScript 2015
String.prototype[@@iterator]()
[...str].length <= 20
[..."café"].length
[..."café"].length
vs.
[..."café"].length
é(U+00e9) = e(U+0065) + ´(U+0301)
两个不同的 Unicode 序列表达了相同的含义,我们就说它们是等价的。
将彼此等价的序列转成同一列序的操作被称为 Unicode 正规化(Unicode Normalize)。
正规化得到的序列在 Unicode 标准中称作正规形式。
ES 2015+ 中可以使用 String.prototype.normalize()
方法。
序列 café
的正规形式
[...str.normalize()].length <= 20
[..."👨👩👦".normalize()].length
👨👩👦 = 👨(U+1F468) + [ZWJ](U+200D) + 👩(U+1F469) + [ZWJ](U+200D) +
👦(U+1F466)
[..."👍🏻👍🏼👍🏽👍🏾👍🏿".normalize()].length
👍🏿 = 👍(U+1F44D) + [Emoji Modifier Fitzpatrick Type-6](U+1F3FF)
其实都是 JavaScript 的锅
len("🤣")
// 1
len("👨👩👦")
// 5
"🤣".count
// 1
"👨👩👦".count
// 1