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 Bynens、Dan Ehrenberg、Claude Pache、Brian Terlson、Thomas Wood、Gorkem Yakin 和 Irregexp 专家 Erik Corry;还有为语言规范和 V8 对这些功能的实现做出贡献的所有其他人。
希望您和我们一样,对这些新的正则表达式功能满怀期待!