JS 中正则表达式的“坑”
“坑”在哪?
这篇文章的主角是 JS 正则表达式中的 test
方法,这个方法是用来检查正则表达式是否能匹配上给定字符串的,在我发现这个问题之前,我的使用方式是:
let re = /javascript/g
re.test('Hello, javascript')
re.test('Hi, javascript')
我期望得到的结果是两个 true
,但实际的结果并不是这样的:
我的第一反应是正则表达式写错了,于是在 Regex101 上测试了一遍,但没有发现问题。
问题定位
如果正则表达式没有问题,那出问题的可能是 test
本身?查看 MDN 的相关页面后,我发现 test
的行为并不像想象中那么简单,我遇到的问题只会在使用了全局标志 g
后出现,如图:
那么全局标志 g
为什么会影响结果呢,实际上这里每次调用 test
,使用的都是同一个正则对象 re
,test
和 exec
都会修改 re
中的 lastIndex
字段,这个字段表示开始匹配的位置。
let re = /javascript/g
re.test('Hello, javascript')
re.test('Hi, javascript')
re.test('Hello, javascript')
那么上面这段代码的运行过程是:
lastIndex
一开始是 0,表示从字符串的开头进行匹配,在 7 的位置发现了第一个“JavaScript”,这时候lastIndex
就到了匹配结果的末尾 17。- 第二次调用
test
时就会从现有的lastIndex
(17)开始,谁知道第二个字符串比较短,17 早就超过边界了,那当然匹配不到了,所以返回了false
。 - 第三次调用时,查看
lastIndex
,在上次匹配失败后被重置为了 0,所以这次是能正确匹配的。
解决方法
- 使用字符串的
match
方法代替,'javascript'.match(/javascript/g)
。 - 在每次调用
test
前,都手动清空一遍共用正则表达式对象re
的lastIndex
。 - 不要使用共用的正则表达式对象,每次
test
前都新建一个re
对象。 - 考虑不使用全局标志
g
。