前段时间,同事"芭比Q"遇到了一个关于正则表达式中的问题,“芭比Q”因为这个坑差点芭比Q了...
问题是这样的
“芭比Q”发现同一正则表达式在处理同一字符串时返回值居然会不同 (ÒωÓױ)!
const reg = /hello/g;
console.log(reg.test('hello')); // true
console.log(reg.test('hello')); // false
console.log(reg.test('hello')); // true
console.log(reg.test('hello')); // false
怎么回事
“芭比Q”经过一番冷静分析后,觉得表达式中的 g 标志参数嫌疑很大,于是他决定把这个参数去掉试试
const reg = /hello/;
console.log(reg.test('hello')); // true
console.log(reg.test('hello')); // true
console.log(reg.test('hello')); // true
console.log(reg.test('hello')); // true
好家伙,这下输出结果终于都一致且符合预期了,居然是 g 搞出的事情,那么为什么加上 g 标志参数后会出现这种现象呢?其他标志参数也会产生类似的问题吗?“芭比Q”决定一探究竟。
一探究竟
我们知道 g 标志参数是用来进行全局搜索的,一般是和 exec() 方法搭配使用,进行多次匹配可以将符合正则表达式中的字符串遍历出来,举个例子:
const reg = /(hello)/g;
const input = 'hello world, hello RegExp, I am babiQ';
let result;
while ((result = reg.exec(input)) !== null) {
console.log(`匹配到 ${result[0]}, 下一次开始匹配的位置是 ${reg.lastIndex}`);
// "匹配到 hello, 下一次开始匹配的位置是 5"
// "匹配到 hello, 下一次开始匹配的位置是 18"
}
例子中的 lastIndex 和 g 标志参数其实关系十分紧密,我们可以看到例子中的 lastIndex 值在每次匹配之后都发生了修改,这是因为 RegExp 对象会将上次匹配成功后的位置记录在 lastIndex 属性中,如果匹配失败,就会将 lastIndex 重置为 0。
知道这些背景之后,回到最开始的问题,其实是因为“芭比Q”设置了 /g,因此 lastIndex 属性会记录下次开始匹配的位置,导致这个意想不到的结果:
const reg = /hello/g;
console.log(`lastIndex 初始值为 ${reg.lastIndex}`);
let result = reg.test('hello');
console.log(`匹配结果为 ${result}, 此时 lastIndex 值为 ${reg.lastIndex}`);
result = reg.test('hello');
console.log(`匹配结果为 ${result}, 此时 lastIndex 值为 ${reg.lastIndex}`);
result = reg.test('hello');
console.log(`匹配结果为 ${result}, 此时 lastIndex 值为 ${reg.lastIndex}`);
result = reg.test('hello');
console.log(`匹配结果为 ${result}, 此时 lastIndex 值为 ${reg.lastIndex}`);
// output:
// "lastIndex 初始值为 0"
// "匹配结果为 true, 此时 lastIndex 值为 5"(经过一次匹配后,lastIndex 变为了 5,因此下次进行匹配时会从字符串下标为 5 的位置开始进行匹配)
// "匹配结果为 false, 此时 lastIndex 值为 0"(经过二次匹配,此时从下标为 5 的位置开始匹配,没有匹配到的值,因此 lastIndex 重置为 0,下次从字符串下标为 0 的位置开始进行匹配)
// "匹配结果为 true, 此时 lastIndex 值为 5"(经过三次匹配后,此时从下标为 0 的位置开始匹配,匹配到了值,因此 lastIndex 置为 5,下次从下标为 5 的位置开始进行匹配)
// "匹配结果为 false, 此时 lastIndex 值为 0"
如何解决
事实上,只有使用了 g 标志参数,lastIndex 属性才会起作用。
再次回到“芭比Q”遇到的这个问题,实际上“芭比Q”使用这个正则只是想要判断字符串中是否含有 hello 字符串,如果含有的话,就去进行下一步的操作,这种情况下其实不使用 /g 也是能够进行判断的,反而使用了 /g 后还会造成意想不到的结果,所以在使用 g 标志参数时需要思考清楚:
-
你的正则表达式是否真的需要使用
/g -
如果确实需要使用到
/g,思考每次在使用test()方法时是否需要将lastIndex重置为 0