String.prototype.normalize()

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2016.

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

参见