JavaScript正则核心在于理解标志位、捕获组及替换符号含义;创建用字面量或RegExp构造函数(注意双反斜杠);match()行为依g和捕获组而异;replace()中$有特殊意义;慎用正则解析结构化文本。
JavaScript 中正则匹配和替换的核心是 RegExp 对象和字符串的 match()、replace() 等方法。关键不在“会不会写正则”,而在于“是否理解标志位、捕获组、替换字符串中特殊符号的含义”——多数问题都出在这里。
两种创建方式:字面量 /pattern/flags 和构造函数 new RegExp(pattern, flags)。前者更常用,但动态拼接时必须用后者;注意构造函数中反斜杠要双写。
/\d+/g ✅,写成 "/\d+/g" ❌(变成普通字符串)new RegExp("\\d+", "g") ✅,new RegExp("\d+", "g") ❌(\d 被 JS 字符串提前解析为非法转义)g 标志,否则 match() 只返回第一个结果且忽略后续i,多行模式用 m(影响 ^ 和 $),但 m 在 JS 中对 . 无影响(不匹配换行符)String.prototype.match() 的三种行为差异返回值类型取决于正则是否有 g 标
志和是否使用括号捕获:
g:返回纯匹配数组,如 "a1b2c3".match(/\d/g) → ["1", "2", "3"]
g 且无捕获组:返回单个匹配对象,含 index 和 input 属性,如 "a1b2".match(/\d/) → ["1", index: 1, input: "a1b2"]
g 但有捕获组:返回带子匹配的数组,["完整匹配", "组1", "组2"],如 "abc".match(/(a)(b)/) → ["ab", "a", "b"]
想稳定获取所有捕获结果?别依赖 match(),改用 matchAll()(返回迭代器,需展开):
const str = "id:123,name:abc";
const regex = /(\w+):(\w+)/g;
[...str.matchAll(regex)].map(m => ({ key: m[1], value: m[2] }));
// → [{ key: "id", value: "123" }, { key: "name", value: "abc" }]
String.prototype.replace() 中的替换逻辑陷阱替换值可以是字符串或函数,但字符串里的 $ 符号有特殊含义,极易误用:
$& 表示整个匹配,$1 表示第一个捕获组,$` 是匹配前内容,$' 是匹配后内容$1?必须写成 $$1(两个 $ → 一个字面量 $)(match, group1, group2, ..., offset, string),其中 match 是完整匹配字符串g 标志,否则只替换第一个示例:把 url("path/to.css") 中的路径提取出来并补上前缀:
const css = 'body { background: url("main.css"); }';
css.replace(/url\("([^"]+)"\)/g, (m, path) => `url("/static/${path}")`);
// → 'body { background: url("/static/main.css"); }'
正则本身开销不大,但不当用法会引发意外行为:
replace() 对空字符串匹配(如 /^/g)会在每个字符前插入内容,小心无限循环风险matchAll() 不支持 IE,需要 polyfill 或降级为 exec() 循环/(? 这类负向断言前先确认执行环境支持(Node.js ≥ 9.0,Chrome ≥ 62)
u 标志,否则 . 和 \w 可能无法正确匹配真正难的不是写出正则,而是判断该不该用正则——比如解析 HTML 用 DOMParser,解析 CSV 用专用库,别硬套 split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/) 这种“正则奇迹”。