正则表达式是描述字符模式的文本规则。在JavaScript中,它并不是一套独立语言,而是通过内置的RegExp对象与字符串方法协同工作的文本处理机制。可以把它理解成一个可配置的扫描器:模式负责回答要找什么,标志位和方法负责回答从哪里找、找到后做什么。比如要提取一段文本中的连续数字,用/\d+/就能描述这个规则,而不需要写循环逐字符判断。

实际开发中,正则表达式常用于表单校验、日志解析、字符串清洗和批量替换。理解它并不需要死记全部语法,关键是建立模式描述、匹配执行、结果处理三个环节的认知。下面围绕创建方式、核心语法、方法调用和常见误区展开。
创建正则对象:字面量与构造函数
JavaScript提供两种创建正则表达式的方式。第一种是字面量写法,直接在两个斜杠之间写模式,例如/ab+c/gi。这种方式在脚本解析阶段就会编译正则,性能更好,适合模式固定的场景。第二种是构造函数写法,通过new RegExp(pattern, flags)创建,适合模式需要动态拼接的情况,比如从用户输入或变量生成规则。
两者的一个重要区别在于反斜杠的转义层级。字面量中写/\d+/即可表示数字字符类;但构造函数接收的是字符串,字符串本身会先处理一次转义,所以要用new RegExp("\\d+", "g"),这里源码中的两个反斜杠会被字符串解析成一个反斜杠,再交给正则引擎。如果只写一个反斜杠,new RegExp("\d+", "g")中的\d会被字符串当作未知转义处理,最终正则退化为字母d,匹配结果完全不同。
标志位是正则对象的另一项配置。常用标志包括:g全局匹配,找到所有结果而不止第一个;i忽略大小写;m多行模式,让^和$匹配每行的开头和结尾;u启用Unicode模式,正确处理四字节字符;y粘性匹配,要求从lastIndex位置精确开始;s让点号匹配换行符。不同标志位组合会明显改变匹配行为,例如使用g后test和exec会维护lastIndex状态。
// 字面量方式
const pattern1 = /ab+c/gi;
// 构造函数方式
const pattern2 = new RegExp("ab+c", "gi");
// 带数字模式的字符串需要双重转义
const pattern3 = new RegExp("\\d+", "g"); // 等价于 /\d+/g
核心语法:字符类、量词、分组与断言
正则表达式的匹配能力来自元字符。字符类是最基础的语法,\d表示数字,\w表示字母、数字和下划线,\s表示空白字符,点号.表示除换行外的任意字符。如果只需要某几个备选字符,可以用方括号自定义,例如[0-9a-f]匹配十六进制字符。方括号内的^表示取反,[^0-9]匹配非数字字符。
量词用来控制匹配次数。*表示零次或多次,+表示一次或多次,?表示零次或一次,{n,m}表示至少n次至多m次。默认情况下量词是贪婪的,会尽量多匹配;在量词后加?可以切换为非贪婪模式,尽量少匹配。例如对字符串a123b456b,模式/a.*b/会匹配到最后一个b,而/a.*?b/只匹配到第一个b。
分组用于提取子串或施加整体量词。圆括号(...)创建捕获组,匹配结果会按顺序保存;如果只希望分组而不捕获,可以使用(?:...)。断言则用于限定位置而非字符,例如^和$匹配字符串边界,\b匹配单词边界,(?=...)是正向先行断言,表示右侧必须满足条件。(?!...)是负向先行断言,表示右侧不能匹配指定模式。这些零宽匹配非常适合做复杂校验而不消耗字符。
const log = '请求时间 10:30:22,耗时 320ms';
const timePattern = /(\d{2}):(\d{2}):(\d{2})/;
const matched = log.match(timePattern);
console.log(matched[0]); // 10:30:22
console.log(matched[1]); // 10
console.log(matched[2]); // 30
console.log(matched[3]); // 22
const input = 'A-1024';
const output = input.replace(/([A-Z])-(\d+)/, '$2-$1');
console.log(output); // 1024-A
用test、exec、match、replace和split处理文本
字符串方法中最常用的是match和replace。match在不带g标志时返回第一个匹配及其捕获组,带g时返回所有完整匹配的数组,但不再包含捕获组细节。replace允许使用捕获组引用进行重组,第二个参数中的$1、$2分别代表第一个和第二个捕获组。split可以按正则拆分字符串,适合处理分隔符不固定的文本。
正则对象自身的test方法返回布尔值,适合快速校验。exec方法则返回匹配详情,包括捕获组、匹配索引和引擎当前扫描位置。需要特别注意的是,全局或粘性标志下,exec和test会从lastIndex位置继续匹配,所以可以用循环遍历所有结果。下面示例展示了如何逐个提取数字,并观察索引变化。
const reg = /\d+/g;
const source = '工单A88,数量3,价格56';
let match;
while ((match = reg.exec(source)) !== null) {
console.log('匹配值:' + match[0], '起始索引:' + match.index, 'lastIndex:' + reg.lastIndex);
}
// 输出:
// 匹配值:88 起始索引:3 lastIndex:5
// 匹配值:3 起始索引:8 lastIndex:9
// 匹配值:56 起始索引:11 lastIndex:13
replace配合正则适合做脱敏、格式化等操作。利用捕获组可以在替换结果中调整顺序或插入固定字符。下面示例把手机号中间四位替换为星号。这里星号只是替换文本,并非正则语法。
const phone = '手机号:13800138000';
const masked = phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
console.log(masked); // 手机号:138****8000
常见误区:lastIndex状态、贪婪匹配与灾难性回溯
很多人第一次使用带g标志的正则时会遇到一个奇怪现象:同一个正则对象连续调用test,结果一会是true一会是false。原因就是lastIndex在每次匹配后被更新到下一次开始的位置。如果目标字符串不变,第二次调用从上次结束位置继续找,找不到就会返回false,同时lastIndex重置为0。解决方式有两个:每次调用前手动重置lastIndex = 0,或者在不需要状态的情况下不添加g标志。
const re = /a/g;
console.log(re.test('ba')); // true
console.log(re.test('ba')); // false
console.log(re.test('ba')); // true
另一个容易混淆的是贪婪与非贪婪。默认量词会尽量多匹配,这在提取两个标记之间的内容时可能得到超出预期的结果。比如在a123b456b中,/a.*b/会跨过第一个b直到最后一个b;如果只想匹配到第一个b,应写成/a.*?b/。判断何时使用非贪婪,可以看数据中是否存在重复的结束标记。
const text = 'a123b456b'; const greedy = text.match(/a.*b/); const lazy = text.match(/a.*?b/); console.log(greedy[0]); // a123b456b console.log(lazy[0]); // a123b
灾难性回溯是正则性能问题的典型来源。当多个量词嵌套且相互覆盖时,例如/(a+)+b/,对以大量a结尾且没有b的字符串进行匹配,引擎会尝试指数级组合路径,可能阻塞主线程。实际项目中应避免类似(a+)+、([a-zA-Z]+)*这类复合量词。对用户输入做正则校验时,最好限制输入长度,并优先使用简单、线性的模式。性能敏感的循环中,还应避免在循环体内重复创建正则对象,尽量把编译好的字面量提到循环外。
JavaScript正则表达式把模式描述和字符串方法结合起来,适合处理各种文本任务。掌握创建方式、元字符与方法调用后,再注意lastIndex、非贪婪和回溯问题,就能在大多数场景中写出准确且可维护的匹配逻辑。
JavaScript正则表达式正则匹配文本处理修改时间:2026-09-24 19:19:40