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

ES2015 为 JavaScript 语言引入了许多新功能,包括通过 Unicode (/u) 和粘性 (/y) 标志对正则表达式语法进行了重大改进。但自那以后,我们一直在不断改进。V8 团队与 TC39(ECMAScript 标准机构)的其他成员密切合作,提出并共同设计了多项新功能,使正则表达式变得更加强大。

这些功能目前正在提议纳入 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 标志

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

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

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

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

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

Unicode 属性转义

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

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

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

/\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 属性类的完整列表。如需查看更多示例,请参阅这篇信息丰富的文章

回溯断言

从一开始,预测断言就已是 JavaScript 正则表达式语法的一部分。我们终于引入了与之对应的回溯断言。有些人可能还记得,这项功能已经在 V8 中存在很长一段时间了。我们甚至在后台使用回溯断言来实现 ES2015 中指定的 Unicode 标志。

该名称已经很好地描述了其含义。它提供了一种方法,可限制某个模式仅在其前面有回溯组中的模式时才匹配。它既有匹配的变种,也有不匹配的变种:

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

如需了解详情,请参阅我们专门介绍 lookbehind 断言的之前的博文,以及相关 V8 测试用例中的示例。

致谢

本文不提及为实现这一目标而付出辛勤努力的部分人员,就显得有些不全了:尤其是语言专家 Mathias BynensDan EhrenbergClaude PacheBrian TerlsonThomas Wood、Gorkem Yakin 和 Irregexp 专家 Erik Corry;还有为语言规范和 V8 对这些功能的实现做出贡献的所有其他人。

希望您和我们一样,对这些新的正则表达式功能满怀期待!