即将推出的正则表达式功能

Jakob Gruber
郭阳

ES2015 为 JavaScript 语言引入了许多新功能,包括使用 Unicode (/u) 和粘性 (/y) 标志对正则表达式语法进行了重大改进。但从那以后,开发工作并没有就此停止。通过与 TC39(ECMAScript 标准正文)的其他成员密切协作,V8 团队提出并联合设计了多项新功能,以使正则表达式更加强大。

目前,我们正提议将这些功能纳入 JavaScript 规范。虽然这些提案尚未被完全接受,但它们已处于 TC39 流程的第 3 阶段。我们通过一个标志实现了这些功能(见下文),以便在最终确定规范之前向相应的提案作者及时提供设计和实现反馈。

这篇博文为您提前介绍了这一激动人心的未来。如果您想继续学习后面的示例,请前往 chrome://flags/#enable-javascript-harmony 启用实验性 JavaScript 功能。

已命名的捕获

正则表达式可以包含所谓的捕获(或组),它可以捕获一部分匹配文本。到目前为止,开发者只能通过其索引来引用这些捕获,索引由捕获在模式中的位置决定。

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'

众所周知,正则表达式已经非常难以读取、写入和维护,并且数字引用可能会进一步增加复杂性。例如,在较长的模式下,确定特定拍摄的索引可能会比较困难:

/(?:(.)(.(?<=[^(])(.)))/  // Index of the last capture?

更糟糕的是,更改模式可能会改变所有现有捕获的索引:

/(a)(b)(c)\3\2\1/     // A few simple numbered backreferences.
/(.)(a)(b)(c)\4\3\2/  // All need to be updated.

“已命名的捕获”是一项即将推出的功能,它允许开发者为捕获指定名称,从而帮助缓解这些问题。其语法类似于 Perl、Java、.Net 和 Ruby:

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'

命名的捕获还可以通过命名的反向引用和 String.prototype.replace 进行引用:

// Named backreferences.
/(?<LowerCaseX>x)y\k<LowerCaseX>/.test('xyx');  // true

// String replacement.
const pattern = /(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern, '$<snd>$<fst>');                              // 'ba'
'ab'.replace(pattern, (m, p1, p2, o, s, {fst, snd}) => fst + snd);  // 'ba'

如需了解这项新功能的完整详情,请参阅规范提案

dotAll 标志

默认情况下,正则表达式中的 . Atom 匹配除行终止符以外的任何字符:

/foo.bar/u.test('foo\nbar');   // false

提案引入了 dotAll 模式,该模式通过 /s 标志启用。在 dotAll 模式下,. 也匹配行终止符。

/foo.bar/su.test('foo\nbar');  // true

如需了解这项新功能的完整详情,请参阅规范提案

Unicode 属性转义

随着 ES2015 中引入的 Unicode 感知功能,突然有更多字符可以视为数字,例如带圆圈的数字 1:1;或者被视为文字字符,例如表示雪的中文字符: 雪。

这两者都无法与 \d\w 匹配。更改这些简写形式的含义会破坏现有的正则表达式模式。

不过,我们引入了新的属性转义序列。 请注意,它们仅适用于 Unicode 感知型正则表达式(以 /u 标志表示)。

/\p{Number}/u.test('①');      // true
/\p{Alphabetic}/u.test('雪');  // true

反之也可以使用 \P 来匹配。

/\P{Number}/u.test('①');      // false
/\P{Alphabetic}/u.test('雪');  // false

Unicode 联盟定义了许多其他属性,例如数学符号或日语平假名字符:

/^\p{Math}+$/u.test('∛∞∉');                            // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな');  // true

您可以在当前的规范方案中找到受支持的 Unicode 属性类的完整列表。如需查看更多示例,请参阅这篇信息丰富的文章

Lookback 断言

先行断言从一开始就是 JavaScript 正则表达式语法的一部分。我们最终引入了其对应的后备断言。有人可能还记得,此版本已包含在 V8 中很长时间了。我们甚至在后台使用 Lookbehind 断言来实现 ES2015 中指定的 Unicode 标记。

这个名称已经很好地说明了其含义。它提供了一种方式,可将某个模式限制为仅在后备组中包含该模式后才匹配。它具有匹配和不匹配的变种:

/(?<=\$)\d+/.exec('$1 is worth about ¥123');  // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123');  // ['123']

如需了解详情,请参阅我们上一篇博文(专门介绍后置断言),以及相关的 V8 测试用例中的示例。

致谢

这篇博文并无不提及一些为实现这一目标而努力付出的人员,尤其是语言冠军:Mathias BynensDan EhrenbergClaude PacheBrian TerlsonThomas Wood、Gorkem Yakin 和 Irregexp 高手,Erik Corry 也为这些语言的实施贡献了代码。

希望您和我们一样,对这些新的正则表达式功能感到兴奋!