原文链接:https://javascript.info/regexp-unicode,translate with ❤️ by zhangbao.

    使用 Unicode 标记 /.../u 可以启用正则表达式对查找代理对的支持。

    代理对在 字符串 一章里已经说过了。

    在这里我们再简要的回忆下。简单说,正常一个字符使用 2 个字节编码,这样我们能获得最多 65535 个字符,但是后来有更多的字符出现了,用 2 个字节已经不够表示了。

    所以,后来的字符使用了 4 个字节编码,像 𝒳(数学里的 X)或者😄(微笑)。

    下面我们来比较下不同字符的 Unicode 码:

    字符 Unicode 字节
    a 0x0061 2
    0x2248 2
    𝒳 0x1d4d3 4
    𝒴 0x1d4d4 4
    😄 0x1f604 4

    a 这样的字符占据 2 个字节,其他字符占据 4 个字节。

    Unicode 是这样做的,4字节的字符表示一个整体的含义。

    过去 JavaScript 并不知道这个,所以现在许多的字符串方法是有问题的。例如,length 属性会认为有两个字符:

    1. alert( '😄'.length ); // 2
    2. alert( '𝒳'.length );// 2

    但是我们看到的只有一个啊,关键是 length 属性只认 2 个字节表示的字符,因为这些字符用 4 个字节表示,所以被 length 属性认为有 2 个字符。这是不对的,他们是一个整体(称为”代理对”)。

    正常情况下,正则表达式也把”长字符”当成是 2 个用 2 个字节表示的字符。

    这会导致奇怪的结果,例如我们从字符串”𝒳“里查找 [𝒳𝒴]

    1. alert( '𝒳'.match(/[𝒳𝒴]/) ); // 奇怪的结果

    结果是错误的,因为默认正则引擎并不理解代理对。它认为 [𝒳𝒴] 并不是两个字符,而是四个字符:𝒳 的左半部分(1),𝒳 的右半部分(2),𝒴 字符的左半部分(3),𝒴 字符的右半部分(4)。

    所以正则引擎在字符串 𝒳 中查找的是 𝒳 的左边一半,而不是整个符号。

    换句话说,搜索类似 '12'.match(/[1234]/) —— 返回了 1𝒳 的左边一半)。

    /.../u 解决了这个问题,它启用了正则表达式查找代理对的能力,所以返回的结果是正确的:

    1. alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳

    如果我们忘记这个标记的话,就会发生错误:

    1. '𝒳'.match(/[𝒳-𝒴]/); // SyntaxError: invalid range in character class

    这里的正则 [𝒳-𝒴] 可以看作 [12-34]2𝒳 的右半部分,3𝒴 的左半部分)。23 之间所代表的范围并不是可接受的。

    当然使用标记的话就解决了这个问题:

    1. alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴

    最后,让我们注意到,如果我们无需处理代理对的话,/.../u 标记对我们没有任何作用。但在现代社会,我们经常遇到他们。

    (完)