必须加g标志才能全量扫描,因不带g时正则默认只匹配首个且lastindex恒为0;加g后lastindex可读写并自动更新,配合exec()、matchall()或replace()回调即可提取各匹配项的索引、长度与内容。

要利用正则表达式的 global(g)标志实现对原始文本的全量扫描与索引提取,关键在于理解 g 如何改变匹配行为,并配合支持全局迭代的方法(如 exec()、match() 或 replace() 回调),从而捕获每个匹配项的位置(index)、长度(length)和内容。
为什么必须加 g 才能全量扫描
不带 g 标志的正则表达式默认只找第一个匹配,后续调用(如多次 exec())总会从开头重复查找,lastIndex 始终为 0。加上 g 后:
-
lastIndex变为可读写状态,每次成功匹配后自动更新为匹配结束位置 -
exec()能持续向后推进,直到返回null -
match()返回完整匹配数组(而非仅首个) -
replace()对所有匹配生效,且回调函数中可访问match.index
用 exec() + lastIndex 提取每个匹配的索引与内容
这是最灵活、可控性最强的方式,适用于需精确控制扫描流程或处理重叠/边界敏感模式的场景:
- 创建带
g的正则对象(推荐字面量写法:/pattern/g) - 循环调用
exec(str),每次返回MatchResult对象 - 从结果中提取
.index(起始位置)、[0](匹配字符串)、.length(即.index + [0].length为结束位置)
示例:
const str = "abc123def456ghi789";const re = /\d+/g;
let result;
const matches = [];
while ((result = re.exec(str)) !== null) {
matches.push({
value: result[0],
start: result.index,
end: result.index + result[0].length
});
}
// → [{value:"123",start:3,end:6}, {value:"456",start:9,end:12}, {value:"789",start:15,end:18}]
用 match() 获取全部匹配值(但不直接提供索引)
str.match(/pattern/g) 最简捷,适合只需内容、无需位置信息的场景。若还需索引,可结合 lastIndex 手动推算,或改用 matchAll():
-
match()返回纯字符串数组,无位置信息 -
matchAll()返回迭代器,每个元素是标准exec()结果,含index、groups等完整字段,且天然支持g
推荐写法:
const matches = [...str.matchAll(/\d+/g)];// 每个 item 是 MatchResult:item.index, item[0], item.groups 等
在 replace() 回调中同步提取索引
当替换逻辑与索引提取耦合时(例如高亮关键词并记录位置),replace() 的回调函数会传入匹配项、分组、索引和原字符串:
- 回调签名:
(match, p1, p2, offset, string) => {...} -
offset即当前匹配的起始索引 - 可在此处收集位置、修改匹配内容、或触发副作用(如存入 Map)
示例(记录所有数字位置并替换为带标记的版本):
const positions = [];const newStr = str.replace(/\d+/g, (m, _, __, i) => {
positions.push({ value: m, start: i });
return `[${m}]`;
});
掌握这三种方式,就能根据实际需求选择最合适的路径:追求简洁用 matchAll(),需要精细控制用 exec(),边替换边采集用 replace() 回调。核心前提是——g 不可省略,否则永远只停在第一个匹配。











