String.prototype.normalize()

Stringnormalize() 方法返回该字符串的 Unicode 标准化形式。

尝试一下

语法

js
normalize()
normalize(form)

参数

form 可选

"NFC""NFD""NFKC""NFKD" 其中之一,用于指定 Unicode 标准化形式。如果省略或为 undefined,则使用 "NFC"

这些值具有以下含义:

"NFC"

规范分解,然后进行规范组合。

"NFD"

规范分解。

"NFKC"

兼容分解,然后进行规范组合。

"NFKD"

兼容分解。

返回值

一个包含给定字符串的 Unicode 标准化形式的字符串。

异常

RangeError

如果 form 不是上述指定的值之一,将抛出该异常。

描述

Unicode 为每个字符分配一个唯一的数值,称为码位。例如,字母 "A" 的码位被表示为 U+0041。然而,有时候一个抽象字符可以由一个或多个码位或码位序列来表示,比如字母 "ñ" 可以被以下任意一种方式表示:

  • 单个码位 U+00F1。
  • 字母 "n" 的码位(U+006E)后跟组合波浪符的码位(U+0303)。
js
const string1 = "\u00F1";
const string2 = "\u006E\u0303";

console.log(string1); // ñ
console.log(string2); // ñ

然而,由于码位不同,字符串比较不会将它们视为相等。而且由于每个版本中的码位数量不同,它们甚至具有不同的长度。

js
const string1 = "\u00F1"; // ñ
const string2 = "\u006E\u0303"; // ñ

console.log(string1 === string2); // false
console.log(string1.length); // 1
console.log(string2.length); // 2

normalize() 方法将字符串转换为一种标准化形式,这有助于解决这个问题,该标准化形式适用于表示相同字符的所有码位序列。有两种主要的标准化形式,一种基于规范等价性,另一种基于兼容性

规范等价标准化

在 Unicode 中,如果两个码位序列表示相同的抽象字符,并且它们应该始终具有相同的视觉外观和行为(例如,它们应该始终以相同的方式进行排序),则这两个序列具有规范等价性。

你可以使用 normalize() 方法并使用 "NFD""NFC" 参数来生成一个字符串的形式,该形式对于所有规范等价的字符串都是相同的。在下面的示例中,我们对字符 "ñ" 的两种表示进行标准化:

js
let string1 = "\u00F1"; // ñ
let string2 = "\u006E\u0303"; // ñ

string1 = string1.normalize("NFD");
string2 = string2.normalize("NFD");

console.log(string1 === string2); // true
console.log(string1.length); // 2
console.log(string2.length); // 2

组合和分解形式

请注意,在 "NFD" 下,标准化形式的长度为 2。这是因为 "NFD" 给出了规范形式的分解版本,其中单个码位被拆分为多个组合码位。对于 "ñ",其分解的规范形式是 "\u006E\u0303"

你可以指定 "NFC" 来获取组合的规范形式,其中多个码位在可行的情况下被替换为单个码位。对于 "ñ",其组合的规范形式是 "\u00F1"

js
let string1 = "\u00F1"; // ñ
let string2 = "\u006E\u0303"; // ñ

string1 = string1.normalize("NFC");
string2 = string2.normalize("NFC");

console.log(string1 === string2); // true
console.log(string1.length); // 1
console.log(string2.length); // 1
console.log(string2.codePointAt(0).toString(16)); // f1

兼容标准化

在 Unicode 中,如果两个码位序列表示相同的抽象字符,并且在某些(但不一定是所有)应用程序中应该被类似地处理,则这两个序列是兼容的。

所有规范等价的序列也是兼容的,但反之不成立。

例如:

  • 码位 U+FB00 表示连字 "ff"。它与连续的两个码位 U+0066("ff")兼容。
  • 码位 U+24B9 表示符号 "Ⓓ"。它与码位 U+0044("D")兼容。

在某些方面(例如排序)它们应该被视为等价的,而在某些方面(例如视觉外观)则不应该,因此它们不是规范等价的。

你可以使用 normalize() 方法并使用 "NFKD""NFKC" 参数来生成一个字符串的形式,该形式对于所有兼容的字符串都是相同的:

js
let string1 = "\uFB00";
let string2 = "\u0066\u0066";

console.log(string1); // ff
console.log(string2); // ff
console.log(string1 === string2); // false
console.log(string1.length); // 1
console.log(string2.length); // 2

string1 = string1.normalize("NFKD");
string2 = string2.normalize("NFKD");

console.log(string1); // ff <- 视觉外观改变了
console.log(string2); // ff
console.log(string1 === string2); // true
console.log(string1.length); // 2
console.log(string2.length); // 2

在应用兼容性标准化时,重要的是要考虑你打算如何使用这些字符串,因为标准化形式可能不适用于所有应用程序。在上面的示例中,标准化适用于搜索,因为它使用户可以通过搜索 "f" 来找到字符串。但是,对于显示来说可能不合适,因为视觉表示是不同的。

与规范化标准化一样,你可以通过分别传递 "NFKD""NFKC" 来请求分解或组合的兼容形式。

示例

使用 normalize()

js
// 初始字符串

// U+1E9B: 上方带一个点的拉丁小写字母长 S
// U+0323: 下方组合一个点
const str = "\u1E9B\u0323";

// 规范组合形式(NFC)

// U+1E9B: 上方带一个点的拉丁小写字母长 S
// U+0323: 下方组合一个点
str.normalize("NFC"); // '\u1E9B\u0323'
str.normalize(); // 同上

// 规范分解形式(NFD)

// U+017F: 拉丁小写字母长 S
// U+0323: 下方组合一个点
// U+0307: 上方组合一个点
str.normalize("NFD"); // '\u017F\u0323\u0307'

// 兼容组合(NFKC)

// U+1E69: 拉丁小写字母 S 在上方和下方带一个点
str.normalize("NFKC"); // '\u1E69'

// 兼容分解(NFKD)

// U+0073: 拉丁小写字母 S
// U+0323: 下方组合一个点
// U+0307: 上方组合一个点
str.normalize("NFKD"); // '\u0073\u0323\u0307'

规范

Specification
ECMAScript Language Specification
# sec-string.prototype.normalize

浏览器兼容性

BCD tables only load in the browser

参见