如何在JavaScript中正确比较字符串

1,125 阅读3分钟

比较JavaScript字符串是安全的吗?

让我们比较一下2个字符串str1str2

const str1 = 'Hello!';
const str2 = 'Hello!';
str1 === str2; // => true

因为str1str2 有相同的字符,所以这些字符串是相等的。

2个看起来相同的字符串是否总是相等的?让我们再试试另一个例子。

const str1 = 'café';
const str2 = 'café';
str1 === str2; // => false

虽然str1str2 看起来是一样的,但是比较str1 === str2 的结果是false 。这怎么可能呢?

让我们详细了解一下如何在JavaScript中正确比较字符串。在开始之前,我先让你熟悉一下字形(一种书写单位)和组合字符(修改基本字符外观的特殊字符)。

1.什么是字素

看看下面这个字符串,你能说它的内容是什么?

  const str1 = 'café'; 

你可以很容易地看到它有4个字母:小写c小写a小写f小写e与锐利

用户对一个字符作为一个书写单位的思考方式被命名为grapheme。示例字符串café ,包含4个字素。

这里有一个关于字素的正式定义:

字素是在特定书写系统背景下的一个最小的独特的书写单位。

好吧,这很有趣,但它与字符串的安全比较有什么关系呢?有些字素可以用不同的字符序列来呈现。

特别是,有一组特殊的字符被命名为组合字符,它们修改前一个字符以创造新的字形。让我们来详细介绍一下组合字符。

2.什么是组合字符

合并字符是一个适用于前面的基础字符的字符,以创造一个字素。

组合字符包括重音符、变音符、希伯来语点、阿拉伯语元音符号和印度语马特拉。

组合字符总是需要一个基础字符来应用。你应该避免孤立地显示它们。

例如,é 是一个原子性的字形。您可以使用小写e(基础字符),并将其与组合重音◌́(组合字符)相结合,以显示该字形:e + ◌́ = é。

const e1 = 'e\u0301';
e1; // renders as "é"

其中\u0301 是组合字符◌́的unicode转义序列

然而,请注意,同样的é ,可以用小写e与锐利字符的不同方式来表示。

const e2 = 'é';
e2; // renders as "é"

尽管e1e2 呈现相同的字形,然而,它们是不同的字符串值。

const e1 = 'e\u0301';
const e2 = 'é';
e1 === e2; // => false

3.字符串的安全比较

在更好地理解了字母表和组合字符之后,这里有一些在JavaScript中进行更安全的字符串比较的规则。

首先,你可以使用常规比较运算符===== 或实用函数Object.is() ,安全地比较包含基本多语言平面的字符(包括ASCII字符)的字符串。

const str1 = 'Hello!';
const str2 = 'Hello!';
str1 === str2; // => true

str1str2 都包含ASCII字符,所以你可以安全地使用比较运算符来比较它们。

其次,如果你处理的是基本多语言平面以上的字符,包括组合字符,那么你使用===,==Object.is() 来比较字符串就不安全了。你还需要做的是对比较的字符串进行规范化处理。

const str1 = 'café';
const str2 = 'cafe\u0301'; // same as 'café'
str1 === str2;                         // => false
str1.normalize() === str2.normalize(); // => true

简单地说,字符串的规范化使得规范等价的字符串('café''cafe\u0301' 是等价的,因为它们代表相同的字母)有一个唯一的表示('café''cafe\u0301' 都被规范化为一个唯一的'café' )。

4.4.总结

当字符串的字符来自基本多语言平面时,你可以安全地直接比较它们。

然而,如果字符串可能包含组合字符,那么使用string.normalize() 函数将被比较的字符串规范化为相同的形式会更安全。然后在规范化的字符串上进行比较。