一个由于浏览器优化导致的正则表达式直接量bug
最近在修改一个兼容性bug的时候发现了一个由于浏览器优化所导致的bug。先看例子。
html代码
<input type="text" value="" id="textName" />
<br />
<input type="button" value="直接量测试" id="btnCheck" />
<br />
<input type="button" value="非直接量测试" id="btnCheckNewRegExp" />
</div>
js调用代码
$(document).ready(
function () {
//测试
$("#btnCheck").click(
function () {
var input = $("#textName").val();
//validateDirtyChar(input);
alert(validateDirtyChar(input));
}
);
$("#btnCheckNewRegExp").click(
function () {
var input = $("#textName").val();
alert(validateDirtyCharNewRegExp(input));
}
);
}
);
//正则测试
function validateDirtyChar(input) {
if (input == '') return false;
var result = false;
//1到12位英文字符
var reg = /^[a-zA-Z]{1,12}$/g;
result = reg.test(input);
return result;
}
//非直接量正则测试
function validateDirtyCharNewRegExp(input) {
if (input == '') return false;
var result = false;
var reg =new RegExp("^[a-zA-Z]{1,12}$","g");
result = reg.test(input);
return result;
}
正则很简单,就是测试下是不是英文字符,是不是1-12长度,可是就是这么简单的代码,输入正确的输入,在chorme,FF上测试,直接量按钮连点两下会发现一个诡异的问题,两次的结果不一致,第一次正确,第二次却失败,为什么两次测试的结果会不一致呢?在点击第2个非直接量的测试按钮,发现即使点击多次结果也是正确的,这是为什么?
The String methods search( ), replace( ), and match( ) do not use the lastIndexproperty as exec( ) and test( ) do. In fact, the String methods simply reset lastIndex( ) to 0. If you use exec( ) or test( ) on a pattern that has the g flag set, and you are searching multiple strings, you must either find all the matches in each string so that lastIndex is automatically reset to zero (this happens when the last search fails), or you must explicitly set the lastIndex property to 0 yourself. If you forget to do this, you may start searching a new string at some arbitrary position within the string rather than from the beginning. Finally, remember that this special lastIndex behavior occurs only for regular expressions with the g flag. exec( ) and test( ) ignore the lastIndex property of RegExp objects that do not have the g flag.
这段话是Javascript - The Definitive Guide, 5th Ed (O'Reilly)里描述。正则表达式的全局匹配的情况下,test()方法内部会维护一个lastindex 属性,所以如果我们在一个方法执行两次test的情况下,第一次结果正确,可是第2次lastindex没有归零,会对剩余的字符进行测试,把上面的注释去掉,执行两次就会发现即使输入正确,也会一直显示错,印证了这句描述。那这说明了函数内部的正则表达式直接量还是原来那个,没有新建一个导致这个每次测试都不一致的问题。
在网上google一下,只发现一些零碎的信息浏览器中的 正则表达式陷阱 ,可能问题是出在了浏览器对正则表达式直接量进行优化,也就是不进行垃圾回收而是重用原来的直接量对象。那最简单的避免方法就是少用直接量,尽量用new RegExp生成。其实ECMA-262里面的描述(下面高亮处)本不该出现这样的问题,每次从直接量到正则表达式对象的转换应该是不同的,即使他们内容相同,这个时候浏览器的优化是否有些不合时宜。目前这个问题在IE下并不存在。在一些很标准的浏览器下反倒存在。
A regular expression literal is an input element that is converted to a RegExp object (see 15.10) each time the
literal is evaluated. Two regular expression literals in a program evaluate to regular expression objects that
never compare as === to each other even if the two literals' contents are identical. A RegExp object may also
be created at runtime by new RegExp (see 15.10.4) or calling the RegExp constructor as a function (15.10.3).