**(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 关键字
- 第四节:对象遍历
- 第五节:对象继承
- 第六节:模块
- 第四章:正则表达式
- 第一节:概述
- 第二节:实例属性
- 第三节:实例方法
- 第一课时:测试
- 第二课时:执行
- 第四节:匹配规则
- 第一课时:字面量字符和元字符
- 第二课时:转义符
- 第三课时:特殊字符
- 第四课时:字符类
- 第五课时:预定义模式
- 第六课时:重复类
- 第七课时:量词符
- 第八课时:贪婪模式
- 第九课时:修饰符
- 第十课时:组匹配
- 第五节:字符串的实例方法
- 第一课时:匹配
- 第二课时:搜索
- 第三课时:替换
- 第四课时:分割