// U+00C5:上面带圆圈的大写拉丁字母A console.log(String.fromCharCode(0x00C5));
// U+212B:长度单位“埃” console.log(String.fromCharCode(0x212B));
// Å
// Å
// U+004:大写拉丁字母A
// U+030A:上面加个圆圈 console.log(String.fromCharCode(0x0041, 0x030A)); // Å
比较操作符不在乎字符看起来是什么样的,因此这 3 个字符互不相等。
let a1 = String.fromCharCode(0x00C5),
a2 = String.fromCharCode(0x212B),
a3 = String.fromCharCode(0x0041, 0x030A);
console.log(a1, a2, a3); // Å, Å, Å
console.log(a1 === a2); // false
console.log(a1 === a3); // false
console.log(a2 === a3); // false
为解决这个问题,Unicode 提供了 4 种规范化形式,可以将类似上面的字符规范化为一致的格式,无论 底层字符的代码是什么。这 4 种规范化形式是:NFD(Normalization Form D)、NFC(Normalization Form C)、 NFKD(Normalization Form KD)和 NFKC(Normalization Form KC)。
可以使用 normalize()方法对字 符串应用上述规范化形式,使用时需要传入表示哪种形式的字符串:“NFD”、“NFC”、“NFKD"或"NFKC”。
通过比较字符串与其调用 normalize()的返回值,就可以知道该字符串是否已经规范化了:
let a1 = String.fromCharCode(0x00C5),
a2 = String.fromCharCode(0x212B),
a3 = String.fromCharCode(0x0041, 0x030A);
// U+00C5 是对 0+212B 进行 NFC/NFKC 规范化之后的结果 console.log(a1 === a1.normalize("NFD")); // false console.log(a1 === a1.normalize("NFC")); // true console.log(a1 === a1.normalize("NFKD")); // false console.log(a1 === a1.normalize("NFKC")); // true
// U+212B 是未规范化的
console.log(a2 === a2.normalize("NFD")); // false console.log(a2 === a2.normalize("NFC")); // false console.log(a2 === a2.normalize("NFKD")); // false console.log(a2 === a2.normalize("NFKC")); // false
// U+0041/U+030A 是对 0+212B 进行 NFD/NFKD 规范化之后的结果 console.log(a3 === a3.normalize("NFD")); // true console.log(a3 === a3.normalize("NFC")); // false console.log(a3 === a3.normalize("NFKD")); // true console.log(a3 === a3.normalize("NFKC")); // false
选择同一种规范化形式可以让比较操作符返回正确的结果:
let a1 = String.fromCharCode(0x00C5),
a2 = String.fromCharCode(0x212B),
a3 = String.fromCharCode(0x0041, 0x030A);
console.log(a1.normalize("NFD") === a2.normalize("NFD")); console.log(a2.normalize("NFKC") === a3.normalize("NFKC")); // true console.log(a1.normalize("NFC") === a3.normalize("NFC")); // true