票务监控 的一个应用:高亮原页面关注城市名,原来做法为对每条记录的innerhtml都进行
var reg=/郑州/g; el.innerHTML=el.innerHTML.replace(reg,"....");
想优化一下,避免不必要的DOM操作,同时共享一个正则实例:
var reg=/郑州/g; if(reg.test(el.innerHTML)) el.innerHTML=el.innerHTML.replace(reg,"....");
结果出现了意想不到的结果,抽象如下:
例子:
猜猜下面代码内容:
var reg=/a/g; console.log(reg.test('a')); //console.log(reg.lastIndex); console.log(reg.test('a')); //console.log(reg.lastIndex);
吃惊的话看看下面:
var reg=/a/g; console.log(reg.test('a')); //console.log(reg.lastIndex); console.log(reg.test('ba')); //console.log(reg.lastIndex);
还是要看规范:
15.10.6.3 RegExp.prototype.test(string)
The following steps are taken:
1. Let match be the result of evaluating the RegExp.prototype.exec (15.10.6.3) algorithm upon this
RegExp object using string as the argument.
2. If match is not null, then return true; else return false.
我一直忽略了原来 test 实际上是 :
(r.exec(s) != null)
那么知道exec 对待 g 正则表达式,会记录 lastIndex 到这个正则表达式上,这样子的话 第二个 test 则会从第一个 test 的 lastIndex 开始匹配,出现了上述的结果。
总结原因:
1.test 等于 调用exec
2.lastIndex 存在于正则表达式中,而不是 java 一样有一个 matcher 对象存放 index,或者像 perl 一样 lastIndex 实际上和字符串关联,而javascript 这样子lastIndex和正则表达式关联,test还是最好不要设 g。
3.不成熟的优化等于魔鬼,实际上我可以再单独设一个检测正则表示式即可:
var reg=/郑州/g; var reg2=/郑州/; if(reg2.test(el.innerHTML)) el.innerHTML=el.innerHTML.replace(reg,"....");
PS:共享带来的问题:
有时想为了一点内存,共享几个对象实例,而对于正则表达式则要特别小心,因为正则实例的一些状态比如lastIndex在不同的操作中(test,exec)常常会被影响的,例如:
var script_re = /x/g; var m,x = "avbxz",loop = 1; while (m = script_re.exec(x)) { console.log(script_re.lastIndex + " : " + m[0]); //重置lastIndex要死循环喽 //"y".replace(script_re, ""); //console.log("after replace : "+script_re.lastIndex ); //"y".match(script_re); //console.log("after match : "+script_re.lastIndex ); loop++; if (loop == 100) { alert("die!"); break; } }
updated : 2010-12-10
用 match 取代 test
最好还是不要用 test 来进行判断 :
var reg=/d/g; console.log(reg.test("dd")); console.log(reg.test("dg"));
因为 test 在 //g 会导致lastIndex 记载在该正则表达式中影响后续操作,如果不在乎性能的话,可用 match 取代:
var reg=/d/g; console.log("dd".match(reg)); console.log("dg".match(reg));