Javascript中的“正则表达式对象”与“全局RegExp对象”

2016-07-12

一直以来使用javascript的正则表达式都没太注意这两个词的区别,也一直以来总觉得RegExp的test方法有时候总得不到期望的结果,于是常常以String的search方法取而代之。今天仔细研究了“正则表达式对象”与“全局RegExp对象”的区别,并经过实际测试,总算小有收获。如果你现在也不是很清楚两者的区别,请继续往下看。

先来看一个脚本的例子,你可以运行一下看看结果:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>脚本调试程序</title>
<script language="javascript">
<!--
function t()
{
 var o=/[0-9]/g;      //创建一个正则表达式,匹配一个字符串中的一个数字字符
 document.writeln(o.test("测试本字符串中是否包含字符0。") + "<br/>");
 document.writeln(o.test("测试本字符串中是否包含字符0。") + "<br/>");
 document.writeln(o.test("测试本字符串中是否包含字符0。") + "<br/>");
}
//-->
</script>
<head>
<body onload="t()">
</body>
</html>
运行结果如下:
true
false
true

是不是让你有点惊讶?对同样的字符串进行三次test测试,得到的结果居然是true,false,true。如果你有多个字符串,需要对每个字符串进行同样的正则测试,比如:

for(var k=0;k<aryStr.length;k++)
{
if(o.test(aryStr[k]))
{
 ...
}
else
{
 ...
}
}

那显然你的程序运行结果将和你的意图大相径庭了。
想知道为什么?再运行一下下面的代码就清楚了: 

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>脚本调试程序</title>
<script language="javascript">
<!--
function t()
{
 var o=/[0-9]/g;      //创建一个正则表达式,匹配一个字符串中的一个数字字符
 document.writeln(o.lastIndex + "," + o.test("测试本字符串中是否包含字符0。") + "<br/>");
 document.writeln(o.lastIndex + "," + o.test("测试本字符串中是否包含字符0。") + "<br/>");
 document.writeln(o.lastIndex + "," + o.test("测试本字符串中是否包含字符0。") + "<br/>");
}
//-->
</script>
<head>
<body onload="t()">
</body>
</html>

运行结果如下:
0,true
14,false
0,true

lastIndex属性表示的是正则表达式对象的 "被查找字符串中下一次成功匹配的开始位置"。
可以看出三次调用test()方法时,第一次是从字符串的起始位置开始进行测试,自然测试成功,返回true,而这时lastIndex已经变成匹配成功的位置的下一个字符的索引14了。第二次从第14个字符开始匹配,自然找不到数字字符,因此返回false。由于已经查找到字符串末尾,因此lastIndex属性被修改为0,故第三次匹配测试如同第一次一样,返回true。
知道了为什么,自然就知道解决办法了。

办法一:每次都重新创建正则表达式对象。该方法能解决问题,但效率明显不理想。
办法二:设法每次都让lastIndex复位为0。经测试该属性是可写的,所以只要每次都o.lastIndex=0;即可解决。
办法三:就如开头说的,改用String.search()方法。
最后说一下“正则表达式对象”与“全局RegExp对象”的个人理解(不一定正确哦):如下面的代码一样,你就创建了一个正则表达式对象,它本身只是一个正则表达式信息(或者规则),没有方法,它可以作为其它一些方法的参数来进行匹配,如String.search就是其应用之一。但同时也可以将之作为全局RegExp对象(或者理解成隐式转换为全局RegExp对象,或者理解成由RegExp继承),当你要使用如test(),exec(),lastIndex等方法属性时,实际是将之作为一个全局RegExp对象来使用的。既然是“全局”,那自然上一次的使用将影响下一次,就好象全局变量是一样的道理。用MS的话说,就是:

“全局 RegExp 对象的属性包含不断更新的关于每个匹配出现的信息,而正则表达式对象只包含出现正则表达式匹配的信息。”

如果你不喜欢其工作方式,可改用其它方法,如字符串的search()方法。或每次使用全局RegExp前初始化必要的属性。
var o=/[0-9]/g;