**(1)概述**
正則表達式的括號表示分組匹配,括號中的模式可以用來匹配分組的內容。
~~~
/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true
~~~
上面代碼中,第一個模式沒有括號,結果`+`只表示重復字母`d`,第二個模式有括號,結果`+`就表示匹配`fred`這個詞。
下面是另外一個分組捕獲的例子。
~~~
var m = 'abcabc'.match(/(.)b(.)/);
m
// ['abc', 'a', 'c']
~~~
上面代碼中,正則表達式`/(.)b(.)/`一共使用兩個括號,第一個括號捕獲`a`,第二個括號捕獲`c`。
注意,使用組匹配時,不宜同時使用`g`修飾符,否則`match`方法不會捕獲分組的內容。
~~~
var m = 'abcabc'.match(/(.)b(.)/g);
m // ['abc', 'abc']
~~~
上面代碼使用帶`g`修飾符的正則表達式,結果`match`方法只捕獲了匹配整個表達式的部分。這時必須使用正則表達式的`exec`方法,配合循環,才能讀到每一輪匹配的組捕獲。
~~~
var str = 'abcabc';
var reg = /(.)b(.)/g;
while (true) {
var result = reg.exec(str);
if (!result) break;
console.log(result);
}
// ["abc", "a", "c"]
//?["abc", "a", "c"]
~~~
正則表達式內部,還可以用`\n`引用括號匹配的內容,`n`是從1開始的自然數,表示對應順序的括號。
~~~
/(.)b(.)\1b\2/.test("abcabc")
// true
~~~
上面的代碼中,`\1`表示第一個括號匹配的內容(即`a`),`\2`表示第二個括號匹配的內容(即`c`)。
下面是另外一個例子。
~~~
/y(..)(.)\2\1/.test('yabccab') // true
~~~
括號還可以嵌套。
~~~
/y((..)\2)\1/.test('yabababab') // true
~~~
上面代碼中,`\1`指向外層括號,`\2`指向內層括號。
組匹配非常有用,下面是一個匹配網頁標簽的例子。
~~~
var tagName = /<([^>]+)>[^<]*<\/\1>/;
tagName.exec("<b>bold</b>")[1]
// 'b'
~~~
上面代碼中,圓括號匹配尖括號之中的標簽,而`\1`就表示對應的閉合標簽。
上面代碼略加修改,就能捕獲帶有屬性的標簽。
~~~
var html = '<b class="hello">Hello</b><i>world</i>';
var tag = /<(\w+)([^>]*)>(.*?)<\/\1>/g;
var match = tag.exec(html);
match[1] // "b"
match[2] // " class="hello""
match[3] // "Hello"
match = tag.exec(html);
match[1] // "i"
match[2] // ""
match[3] // "world"
~~~
**(2)非捕獲組**
`(?:x)`稱為非捕獲組(Non-capturing group),表示不返回該組匹配的內容,即匹配的結果中不計入這個括號。
非捕獲組的作用請考慮這樣一個場景,假定需要匹配`foo`或者`foofoo`,正則表達式就應該寫成`/(foo){1, 2}/`,但是這樣會占用一個組匹配。這時,就可以使用非捕獲組,將正則表達式改為`/(?:foo){1, 2}/`,它的作用與前一個正則是一樣的,但是不會單獨輸出括號內部的內容。
請看下面的例子。
~~~
var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]
~~~
上面代碼中的模式,一共使用了兩個括號。其中第一個括號是非捕獲組,所以最后返回的結果中沒有第一個括號,只有第二個括號匹配的內容。
下面是用來分解網址的正則表達式。
~~~
// 正常匹配
var url = /(http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/;
url.exec('http://google.com/');
// ["http://google.com/", "http", "google.com", "/"]
// 非捕獲組匹配
var url = /(?:http|ftp):\/\/([^/\r\n]+)(\/[^\r\n]*)?/;
url.exec('http://google.com/');
// ["http://google.com/", "google.com", "/"]
~~~
上面的代碼中,前一個正則表達式是正常匹配,第一個括號返回網絡協議;后一個正則表達式是非捕獲匹配,返回結果中不包括網絡協議。
**(3)先行斷言**
`x(?=y)`稱為先行斷言(Positive look-ahead),`x`只有在`y`前面才匹配,`y`不會被計入返回結果。比如,要匹配后面跟著百分號的數字,可以寫成`/\d+(?=%)/`。
“先行斷言”中,括號里的部分是不會返回的。
~~~
var m = 'abc'.match(/b(?=c)/);
m // ["b"]
~~~
上面的代碼使用了先行斷言,`b`在`c`前面所以被匹配,但是括號對應的`c`不會被返回。
**(4)先行否定斷言**
`x(?!y)`稱為先行否定斷言(Negative look-ahead),`x`只有不在`y`前面才匹配,`y`不會被計入返回結果。比如,要匹配后面跟的不是百分號的數字,就要寫成`/\d+(?!%)/`。
~~~
/\d+(?!\.)/.exec('3.14')
// ["14"]
~~~
上面代碼中,正則表達式指定,只有不在小數點前面的數字才會被匹配,因此返回的結果就是`14`。
“先行否定斷言”中,括號里的部分是不會返回的。
~~~
var m = 'abd'.match(/b(?!c)/);
m // ['b']
~~~
上面的代碼使用了先行否定斷言,`b`不在`c`前面所以被匹配,而且括號對應的`d`不會被返回。
- 第一章:變量與作用域
- 第一節:變量
- 第二節:執行環境及作用域
- 第三節:JS 解析機制
- 第四節:垃圾收集
- 第二章:深入函數
- 第一節:概述
- 第二節:函數聲明
- 第三節:函數調用
- 第四節:函數參數
- 第五節:函數返回值
- 第三章:面向對象
- 第一節:概述
- 第二節:對象聲明
- 第三節:this 關鍵字
- 第四節:對象遍歷
- 第五節:對象繼承
- 第六節:模塊
- 第四章:正則表達式
- 第一節:概述
- 第二節:實例屬性
- 第三節:實例方法
- 第一課時:測試
- 第二課時:執行
- 第四節:匹配規則
- 第一課時:字面量字符和元字符
- 第二課時:轉義符
- 第三課時:特殊字符
- 第四課時:字符類
- 第五課時:預定義模式
- 第六課時:重復類
- 第七課時:量詞符
- 第八課時:貪婪模式
- 第九課時:修飾符
- 第十課時:組匹配
- 第五節:字符串的實例方法
- 第一課時:匹配
- 第二課時:搜索
- 第三課時:替換
- 第四課時:分割