坑爹的RegExp test()

最近在公司用Javascript实现前端QuickSearch功能,偶尔触及RegExp对象的test()方法。
大致有类似的代码:

1
2
3
4
5
6
7
8
9
10
11
var texts = ["item1", "item2", "item3", "item4", "item5", "item6"];
var pattern = new RegExp("it", "ig");
function check() {
  var len = texts.length, text, isMatched = false;
  for(var i = 0; i < len; i++){
    text = texts[i];
    isMatched = pattern.test(text);

    console.error(text + ": " + isMatched);
  }
}

其结果甚是诡异,把爹坑得郁闷不已。

百思不得其解之下,只好借助网络。终于,在w3schools中找到了问题的关键所在。

原来,RegExp的对象有一个很NB的lastIndex属性,当我们调用方法 exec() 或 test()时,RegExp对象会用它来记录最近一次成功匹配后的位置,并将其作为下一次检索的起始点。只有当方法 exec() 或 test() 再也找不到可以匹配的文本时,它们会自动把 lastIndex 属性重置为 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
var texts = ["item1", "item2", "item3", "item4", "item5", "item6"];
var pattern = new RegExp("it", "ig");
function check() {
var len = texts.length, text, isMatched = false;
for(var i = 0; i < len; i++){
text = texts[i];

var result = "LastIndex: " + pattern.lastIndex;
isMatched = pattern.test(text);
result = result + " " + text + ": " + isMatched;
console.error(result);
}
}

如此,在”item2”, “item4”, “item6”中从索引位置2开始检索关键字“it”,当然是找不到的了。
因为对同一个RegExp对象而言,“终点即起点,执着何用。”显然,我们需要从头开始,从0开始。我们需要在做完一次exec() 或 test()后手动将lastIndex重新设置为0。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var texts = ["item1", "item2", "item3", "item4", "item5", "item6"];
var pattern = new RegExp("it", "ig");
function check() {
var len = texts.length, text, isMatched = false;
for(var i = 0; i < len; i++){
text = texts[i];

var result = "LastIndex: " + pattern.lastIndex;
isMatched = pattern.test(text);
result = result + " " + text + ": " + isMatched;
console.error(result);

pattern.lastIndex = 0;
}
}

惟有如此,事可成也。

其实所谓“坑爹”之说,不过是遇事未能详查,徒添困扰罢了。w3schools在对RegExp的lastIndex进行解说时早已明确写明:

提示和注释

  • 重要事项:不具有标志 g 和不表示全局模式的 RegExp 对象不能使用 lastIndex 属性。

  • 提示:如果在成功地匹配了某个字符串之后就开始检索另一个新的字符串,需要手动地把这个属性设置为 0。

今记录于此,以鉴后来之事。

显示 Gitment 评论