记 RegExp.test() 方法使用全局匹配时一会返回 true 一会返回 false 的问题

今天小伙伴问了一个奇怪的问题,他在使用 RegExp.test() 方法时使用全局匹配时会依次返回 truefalse 的现象。
比如说:

var t = /#/g
t.test("#")
// true
t.test("#")
// false
t.test("#")
// true
t.test("#")
// false

我一开始以为是他正则写的不对,让他把其它的匹配规则都去掉再试试,结果问题还是依旧。
后来让他把 /gg 去掉试了下发现没问题了。但还是没有怀疑到全局匹配的问题上,以为是他哪里覆写掉了的 test() 方法。让他使用空白页测试,依旧有这样的问题。

所以应该就是全局匹配的问题。检索了以下相关问题,发现正则表达式使用了 g 全局检索是,其会内置一个 lastIndex 属性,并且这个属性并不会被重置,会在下一次使用时保留(如果index值小于或等于字符串长度时)

其实在 MDN 文档上面也提到了这个问题 Using test() on a regex with the “global” flag

const regex = /foo/g; // the "global" flag is set

// regex.lastIndex is at 0
regex.test("foo"); // true

// regex.lastIndex is now at 3
regex.test("foo"); // false

// regex.lastIndex is at 0
regex.test("barfoo"); // true

// regex.lastIndex is at 6
regex.test("foobar"); // false

// regex.lastIndex is at 0
regex.test("foobarfoo"); // true

// regex.lastIndex is at 3
regex.test("foobarfoo"); // true

// regex.lastIndex is at 9
regex.test("foobarfoo"); // false

// regex.lastIndex is at 0
// (...and so on)

所以可以通过每次调用后重置掉 lastIndex 值,比如说:

var t = /#/g
t.test("#")
// true
t.lastIndex = 0
t.test("#")
// true
t.lastIndex = 0
t.test("#")
// true
t.lastIndex = 0

但是我觉得很不优雅,可以使每次一都使用新的正则,或者封装在一个方法内来避免

/#/g.test("#")
// true
/#/g.test("#")
// true
/#/g.test("#")
// true
function fn(str){
  const regex = /#/g
  return regex.test(str)
}
fn("#")
// true
fn("#")
// true
fn("#")
// true

其实直接去掉 g 全局匹配的标识就可以了,因为大部分的时间,我们只是想要去测试一下字符串是否匹配我们预设规则而已。

同时大多数的时候我们并不会遇到这样的情况,因为书写在函数内是我们使用最多的情况,写在全局或者函数外部其实使很少见的。

相关阅读

RegExp.prototype.test() - JavaScript | MDN
RegExp: lastIndex - JavaScript | MDN
为什么使用正则test( )第一次是 true,第二次是false? - Andy_alone - 博客园