最近在修改一個相容性bug的時候發現了一個由於瀏覽器最佳化所導致的bug。先看例子。
html代碼
代碼
<div>
<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)裡描述。Regex的全域匹配的情況下,test()方法內部會維護一個lastindex 屬性,所以如果我們在一個方法執行兩次test的情況下,第一次結果正確,可是第2次lastindex沒有歸零,會對剩餘的字元進行測試,把上面的注釋去掉,執行兩次就會發現即使輸入正確,也會一直顯示錯,印證了這句描述。那這說明了函數內部的Regex直接量還是原來那個,沒有建立一個導致這個每次測試都不一致的問題。
在網上google一下,只發現一些零碎的資訊瀏覽器中的 Regex陷阱 ,可能問題是出在了瀏覽器對Regex直接量進行最佳化,也就是不進行記憶體回收而是重用原來的直接量對象。那最簡單的避免方法就是少用直接量,盡量用new RegExp產生。其實ECMA-262裡面的描述(下面高亮處)本不該出現這樣的問題,每次從直接量到Regex對象的轉換應該是不同的,即使他們內容相同,這個時候瀏覽器的最佳化是否有些不合時宜。目前這個問題在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).