|
很多语言都有由基础字符及其上方/下方的标记所组成的符号。
举个例子,字母 a 就是这些字符 àáâäãåā 的基础字符。
大多数常见的“复合”字符在 Unicode 表中都有自己的编码。但不是所有这些字符都有自己的编码,因为可能的组合形式太多了。
为了支持任意的组合,Unicode 标准允许我们使用多个 Unicode 字符:基础字符后跟着一个或多个“装饰”它的“标记”字符。
例如,如果我们在 S 后附加上特殊的“上方的点”字符(编码为 \u0307),则显示为 Ṡ。
alert( 'S\u0307' ); // Ṡ
如果我们需要在字母上方(或下方)添加一个额外的标记 —— 很简单,只需添加必要的标记字符即可。
例如,如果我们继续在后面附加一个“下方的点”符号(编码 \u0323),那么我们将得到一个“上下都有一个点符号的 S”:Ṩ。
就像这样:
alert( 'S\u0307\u0323' ); // Ṩ
这提供了极大的灵活性,但也带来了一个有趣的问题:两个字符可能在视觉上看起来相同,但却使用的是不同的 Unicode 组合。
举个例子:
let s1 = 'S\u0307\u0323'; // Ṩ, S + 上方点符号 + 下方点符号
let s2 = 'S\u0323\u0307'; // Ṩ, S + 下方点符号 + 上方点符号
alert( `s1: ${s1}, s2: ${s2}` );
alert( s1 == s2 ); // 尽管这两个字符在我们看来是相通的,但结果却是 false
“Unicode 规范化”算法可以解决这个问题,该算法将每个字符串转换为单一的“规范的”形式。
可以借助 str.normalize() 实现这一点。
alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
有意思的是,在我们这个例子中,normalize() 将 3 个字符的序列合并为了一个字符:\u1e68(带有上下两个点的 S)。
alert( "S\u0307\u0323".normalize().length ); // 1
alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
但实际并非总是如此。出现这种情况的原因是符号 Ṩ 是“足够常见的”,所以 Unicode 创建者将其囊括在了 Unicode 主表中,并为其提供了对应的编码。
|
|