以往咱们只是习惯于经过数组下标来访问正则匹配到的分组,但分组达到四、5个时,标识起来就会很是麻烦。V8早已实现了正则命名分组提案,只是咱们不多使用,本文将介绍JS的正则命名分组。git
假设要使用正则匹配一个日期的年月日,以往咱们会这样作:github
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/; const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj[1]; // 1999 const month = matchObj[2]; // 12 const day = matchObj[3]; // 31
这里有几个缺点:正则表达式
全部这些问题,均可以经过正则命名分组来解决。数组
如今你只须要给分组里面一个命名标识便可:babel
(?<year>\d{4})
这里,咱们用变量year
标记了上一个捕获组#1。 该名称必须是合法的JavaScript标识符。 匹配后,您能够经过matchObj.groups.year访问捕获的字符串。 插件
让咱们经过命名分组重写前面的代码:code
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj.groups.year; // 1999 const month = matchObj.groups.month; // 12 const day = matchObj.groups.day; // 31
若是正则里面有了命名分组,那么匹配结果会多了一个groups
的属性,这个属性中包含了一切命名分组的捕获结果。配合上解构大法使用又是一股清流:regexp
const {groups: {day, year}} = RE_DATE.exec('1999-12-31'); console.log(year); // 1999 console.log(day); // 31
固然,即便你使用了命名分组,那么返回的结果还能够经过以往的数组下标方式访问:orm
const year2 = matchObj[1]; // 1999 const month2 = matchObj[2]; // 12 const day2 = matchObj[3]; // 31
命名分组具备如下优势:ip
反向引用命名分组\k<name>
看下面这个匹配重复单词的例子:
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/; RE_TWICE.test('abc!abc'); // true RE_TWICE.test('abc!ab'); // false
同时也可使用以往的反向引用方式:
const RE_TWICE = /^(?<word>[a-z]+)!\1$/; RE_TWICE.test('abc!abc'); // true RE_TWICE.test('abc!ab'); // false
字符串方法replace()以两种方式支持命名分组:
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; console.log('1999-12-31'.replace(RE_DATE, '$<month>/$<day>/$<year>')); // 12/31/1999
若是replace不必定是直接返回新的拼接字符串,那么能够看看下面的办法:
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; console.log('1999-12-31'.replace( RE_DATE, (g, y, m, d, offset, input, {year, month, day}) => month+'/'+day+'/'+year)); // 12/31/1999
看看这replace的callback形参密密麻麻看得心慌慌,不少都用不上,那么咱们看看更简单的写法:
console.log('1999-12-31'.replace(RE_DATE, (...args) => { const {year, month, day} = args.slice(-1)[0]; return month+'/'+day+'/'+year; })); // 12/31/1999
这里配合上spread operator直取最后一个参数,再接上一个解构大法,结果又是一股清流。
若是可选的命名组不被匹配,则其属性值被设置为undefined
,但key是仍存在:
const RE_OPT_A = /^(?<as>a+)?$/; const matchObj = RE_OPT_A.exec(''); // We have a match: console.log(matchObj[0] === ''); // true // Group <as> didn’t match anything: console.log(matchObj.groups.as === undefined); // true // But property as exists: console.log('as' in matchObj.groups); // true
分组名不能有重复项:
/(?<foo>a)(?<foo>b)/ // SyntaxError: Duplicate capture group name
反向引用一个不存在的分组名:
/\k<foo>/u // SyntaxError: Invalid named capture referenced /\k<foo>/.test("k<foo>") // true, 非 Unicode 下为了向后兼容,k 前面的 \ 会被丢弃
在 reaplce() 方法的替换字符串中引用一个不存在的分组:
"abc".replace(/(?<foo>.*)/, "$<bar>") // SyntaxError: Invalid replacement string "abc".replace(/(.*)/, "$<bar>") // "$<bar>",不包含命名分组时会向后兼容