问题背景
今天做需求的过程中偶然发现一个诡异的事情:全局匹配g的正则多次执行test()方法会有一会返回true一会返回false的情况
可以用下面这个简单的例子来还原:
const reg = /\.jpg/g;
const arr = [
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg',
'5.jpg',
];
arr.forEach(item => {
console.log(reg.test(item))
});
把这段代码放到控制台执行,我们可以看到执行的结果如下:
这你就要惊讶了,正常输出不是应该都是true吗?怎么一会true一会false?
重点来了: 正则里有个lastIndex属性,就是这个属性导致上面的问题。
lastIndex
lastIndex:
- 用于规定下次匹配的起始位置
- 是上一次匹配文本之后的第一个字符的位置
- 初始值未0,之后的值是上次匹配到的字符串最后一个字符的索引+1
- 当方法 exec() 或 test() 未匹配成功,lastIndex 属性会自动被重置为 0
注意:lastIndex只有正则表达式使用了表示全局检索的g标志,且使用exec()或test()时,该属性才会起作用
了解完lastIndex我们将上面的代码稍作修改:
const reg = /\.jpg/g;
const arr = [
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg',
'5.jpg',
];
arr.forEach(item => {
console.log(reg.test(item), reg.lastIndex)
});
按照上述规则去执行,我们推断打印的结果应该为:
// lastIndex初始值为0,从索引0开始匹配,匹配成功。lastIndex置为匹配到的最后一个字符的索引+1,即5
true,5
// lastIndex值为5,从索引5开始匹配,匹配失败。lastIndex置为0
false,0
// 从索引0开始匹配,匹配成功,lastIndex置为5
true,5
// 从索引5开始匹配,匹配失败,lastIndex置为0
false,0
// 从索引0开始匹配,匹配成功,lastIndex置为5
true,5
这时我们将代码放到控制台执行,可以看到结果和我们推断的一致。
解决问题
解决方式有三种:
- 不使用全局匹配 g
const reg = /\.jpg/;
const arr = [
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg',
'5.jpg',
];
arr.forEach(item => {
console.log(reg.test(item), reg.lastIndex)
});
- 不使用变量存储正则,每次都是用正则表达式去匹配
const arr = [
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg',
'5.jpg',
];
arr.forEach(item => {
console.log(/\.jpg/g.test(item))
});
- 在每次执行之后把lastIndex置为0
const reg = /\.jpg/g;
const arr = [
'1.jpg',
'2.jpg',
'3.jpg',
'4.jpg',
'5.jpg',
];
arr.forEach(item => {
console.log(reg.test(item));
reg.lastIndex = 0;
});