正则 lastIndex 状态坑

带 g 或 y 标志的正则有状态,反复调用 test/exec 时受 lastIndex 影响,导致"第一次 true 第二次 false"的反直觉行为。

#type / concept #status / evergreen #resource / javascript #resource / ecmascript

[!info] related notes

正则 lastIndex 状态坑

gy 标志的正则表达式是有状态的——它会记住上一次匹配结束的位置,存储在 lastIndex 属性里,下一次从那里继续找。

现象

const reg = /\d/g;

console.log(reg.test("1")); // true
console.log(reg.test("1")); // false  ← 反直觉

不是正则写错了,是 lastIndex 在移动:

const reg = /\d/g;
console.log(reg.lastIndex); // 0

console.log(reg.test("1")); // true
console.log(reg.lastIndex); // 1

console.log(reg.test("1")); // false(从 index 1 开始,找不到了)
console.log(reg.lastIndex); // 0(找不到后自动重置)

原因

  • 不带 g/y 的正则没有状态,每次从头开始,不会有这个问题
  • g/y 的正则每次匹配后会更新 lastIndex
  • 找不到时 lastIndex 重置为 0,所以下一次又正常了

常见触发场景

1. 在循环条件里用 test()

const reg = /\d+/g;
while (reg.test("a1b2c3")) {
  // 第一次 true,匹配 "1"
  // 第二次 true,匹配 "2"
  // 第三次 true,匹配 "3"
  // 第四次 false,退出
  // 看起来正常,但语义上容易搞混
}

2. 在 if 里复用同一个正则对象

const reg = /\d+/g;

if (reg.test("abc123")) {
  console.log("有数字");
}

if (reg.test("abc456")) {
  console.log("也有数字"); // 可能不执行!取决于上一次的 lastIndex
}

3. 函数里使用外部正则

const numberReg = /\d+/g;

function hasNumber(str) {
  return numberReg.test(str); // 危险:外部状态影响结果
}

console.log(hasNumber("abc1"));  // true
console.log(hasNumber("abc1"));  // 可能 false

解法

方案 A:不要给判断类操作加 g

如果只是判断”有没有匹配”,通常不需要 g

const reg = /\d+/;  // 不带 g
reg.test("abc123"); // 始终 true
reg.test("abc123"); // 始终 true

方案 B:每次使用前重置 lastIndex

const reg = /\d+/g;
reg.lastIndex = 0;
reg.test("abc123");

方案 C:函数内创建新的正则

function hasNumber(str) {
  return /\d+/g.test(str); // 每次新对象,无状态问题
}

方案 D:用 match() / search() 替代

这些方法每次从头开始,不受 lastIndex 影响:

"abc123".match(/\d+/);   // 始终返回结果
"abc123".search(/\d+/);  // 始终返回索引

总结

操作建议
判断是否匹配test()不要加 g
提取所有匹配match(/…/g)matchAll()
循环提取详情exec() + g,但理解 lastIndex 机制
复用正则对象每次用前 reg.lastIndex = 0
创建于 2026/3/21 更新于 2026/5/27