以往咱們只是習慣於經過數組下標來訪問正則匹配到的分組,但分組達到四、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>",不包含命名分組時會向後兼容