RegExp对象的创建:
常规的正则表达式的创建可用直接量,即斜杠 “/” 括起来的字符。但在要求参数变化的环境下,RegExp()构造函数是更好的选择:
var reg1 = /'\w+'/g; var reg2 = new RegExp('\'\\w+\'','g');
对比两种创建方式,RegExp中的第一个参数为要创建的正则字符串,一方面注意,因为不是直接量的表示形式,因此不用斜杠“ / ”括起来了;而是字符串中必须要对引号“ ‘ ”和转义符号“ \ ”进行二次转义。
此外,无论是直接量还是RegExp()构造函数,都是生成了新的RegExp对象,并将其赋值给变量。
在《Javascript权威指南》一书中讲到,对于正则表达式的直接量,ECMAscript 3规定在每次它时都会返回同一个RegExp对象,因此用直接量创建的正则表达式的会共享一个实例。直到ECMAScript 5才规定每次返回不同的实例。
各浏览器中,IE一直遵守ECMAScript 5中的规定,其他浏览器的较老版本则遵循ECMAScript 3的规定。因此在实际应用中,采取构造函数创建的方法比较安全,或者在使用lastIndex属性时要记得归0。
括号()的使用:
1、分组
即把单独项组合成子表达式统一处理,一般用于?、+、*、{n,m}等的重复处理。见例子:
var reg = /Java(script)?/;
式子里将script进行统一处理。
2、向后引用
即在正则表达式中用“\n”(n代表引用的序号)引用式中前面括号中匹配的文本。见例子:
var reg = /(\d+)[a-z]{3}\1/; //20man20 //20man23 错 //reg = /\1[a-z]{3}(\d+)/; 错
注意 “\n”引用的是前面匹配的文本" 20 ",而不是匹配的正则表达式" \d+ "。另外,JS里只能引用前面的匹配文本,像例子中的将 \1写在括号引用的前面,将不会匹配到任何文本,浏览器中会提示出错。同样,JS里也不支持类似“ (?<name>exp) ”(exp为正则字符)的引用命名规则,只支持数字的引用。
既然提到了分组和引用,如果只想进行分组,而不想引用,则可用 "(?: exp)"的形式,既不匹配文本,也不给引用编号。见例子:
var reg = /(\w{3})(?:\d+)([a-z]{2})\2/; //man7788abab
显然 \2 匹配的是 "ab" 而不是 "7788" 。这样便于分组处理,也加快了查询的效率。
3、子模式匹配
有时我们想直接引用操作括号匹配的本文,那么可以用子模式匹配的功能(权威指南里叫子模式匹配,有点别扭,实际就是用一个变量形式替换匹配的文本)。基本形式是用 '$n'的形式替代匹配编号为n的文本,常用在String对象里的replace()方法,见例子,等号两边交换单词:
var reg = /(\w+)=(\w+)/; var str = ‘love=hate'; str.replace(reg,'$2=$1'); //"hate=love"
次序、贪婪、懒惰:
一般的重复匹配字符如?、+、*、{n,m}在匹配的过程中,采用贪婪匹配的方法,即尽可能多的匹配到结果字符。与之对应的是懒惰匹配,即尽可能少的匹配结果,使用形式只需在重复匹配字符后加上问号" ? "即可,如??、+?、*?、{n,m}?。见例子:
var str = 'goooogle‘; var reg1 = /o+/; //"goooo" var reg2 = /o+?/; //"go"
现在对例子稍加改动:
var str = 'goooogle‘; var reg1 = /o+gle/; //"oooogle" var reg2 = /o+?gle/; //"oooogle"
改过后的例子结果变为相同了,为什么 /o+?gle/ 没有匹配到“ogle”呢?原来正则表达式中总是从左往右进行匹配的,不会从右边获取子串进行匹配。
虽然上面的结果相同,但匹配的原理不太一样。在reg1中,首先o+会匹配所有的"o",然后接着匹配"gle",从而完成整体匹配。而在reg2中,o+?会先匹配一个"o",然后gle在字符串的第2位到第4位(即原串的"ooo")匹配失败。进而回溯至o+?去匹配第二个"o",成功后再在第3位到第4位匹配"gle",以此类推……最后匹配到整个字符串。
总体要记住,从优先级来说,从左往右的次序匹配 > 贪婪 / 懒惰匹配。
零宽断言:
关于零宽断言的总体解释可参考博文《正则表达式30分钟入门教程》,值得注意的是,JS里只支持零宽先行断言。即零宽正预测先行断言"(?=exp)"和零宽负预测先行断言“(?!exp)"。
所谓“零宽”,就是它并不在匹配的结果字符中占据空间。例如“\w","\s"就会占据一个或几个空间,依匹配的字符长度决定。而像”^","$"这种对应的首末位置,不占据空间,零宽就是属于这一类。
所谓“正 / 负预测”,是指断言中要求满足的情况。“正”表示要满足exp,“负”表示要不满足exp的。
所谓"先行",是指被匹配的字符串在前面,零宽断言跟在后面。即串的后一部分是否满足断言。
所谓“断言”,就是判断的条件。
看两种零宽断言的例子:
var str = 'java coffeescript'; var reg1 = /\b\w+(?=script\b)/; //coffee var reg2 = /\b\w+(?!script\b)/; //java
reg1为零宽正预测先行断言,"(?=script\b)"表示某单词需以“script”结尾,它代表着一种条件,不占有任何空间大小。
同样,reg2为零宽负预测先行断言,"(?!script\b)"表示不以“script”结尾的单词。