JavaScript 正则表达式创建、分组与匹配详解

作者:闭包旅人

时间:2026-10-29

浏览:510

本文详细解析JavaScript中正则表达式的两种创建方式及其实例共享差异,深入讲解括号分组的三种用法(分组、向后引用、非捕获分组),阐述贪婪与懒惰匹配的回溯机制,并对比零宽断言的实现。最后重点区分String.match()与RegExp.exec()在全局标志g下的返回结果差异及lastIndex属性的处理,提供实用的代码验证建议。

文章目录

正则表达式对象的创建方式

RegExp对象的创建主要有两种方式。

常规的正则表达式可以使用直接量形式,即用斜杠“/”包裹字符。但在需要动态生成正则表达式的场景中,使用RegExp()构造函数更为灵活和合适。

括号()的三种核心用法


var reg1 = /'\w+'/g;
var reg2 = new RegExp('\'\\w+\'','g');

对比这两种创建方式,使用RegExp构造函数时,第一个参数是正则字符串。由于不是直接量形式,因此不需要用斜杠“/”包裹,但字符串内部的单引号“'”和反斜杠转义符“\”需要进行二次转义,否则会导致语法错误。

无论是使用直接量还是RegExp()构造函数,最终结果都是生成一个新的RegExp对象,并将其赋值给相应的变量。

根据《Javascript权威指南》的描述,在ECMAScript 3规范中,正则直接量每次执行时都会返回同一个RegExp对象实例,这意味着多个变量可能共享同一个正则实例。直到ECMAScript 5规范才明确规定每次使用直接量都会创建不同的实例。

在浏览器兼容性方面,IE浏览器一直遵循ECMAScript 5的规定,每次创建新实例;而其他浏览器的旧版本可能仍遵循ECMAScript 3的规定,共享实例。因此,在实际开发中,使用构造函数创建正则表达式是更安全的做法。如果必须使用直接量且涉及lastIndex属性,务必记得在每次使用后将其重置为0。

括号()在正则表达式中主要有三种用途。

第一种是分组。

分组的作用是将多个单独项组合成一个子表达式,以便统一应用量词如?、+、*、{n,m}等。例如:

var reg = /Java(script)?/;

上述例子中,script被包含在括号内,表示整个script部分是可选的。

第二种是向后引用。

在正则表达式内部,可以使用“\n”(其中n为数字,代表前面第n个括号)来引用之前括号中匹配到的具体文本内容。例如:

分组与向后引用


var reg = /(\d+)[a-z]{3}\1/; 
//20man20
//20man23 错
//reg = /\1[a-z]{3}(\d+)/; 错

需要注意的是,“\n”引用的是前面匹配到的具体文本(如“20”),而不是匹配该文本的正则规则(如“\d+”)。此外,JavaScript只支持数字形式的向后引用,不支持命名捕获组如“(?<name>exp)”的语法。如果将“\1”写在定义它的括号之前,将无法匹配任何内容,浏览器通常会报错。

如果只需要进行逻辑分组而不希望生成捕获组或占用引用编号,可以使用非捕获分组“(?: exp)”。这种形式既不匹配文本,也不分配引用编号。例如:

非捕获分组与子模式匹配


var reg = /(\w{3})(?:\d+)([a-z]{2})\2/;
//man7788abab

在上述例子中,“\2”匹配的是“ab”而不是“7788”。这种方式既实现了分组处理,又提高了查询效率。

第三种是子模式匹配(或称替换引用)。

有时我们需要在字符串操作中使用括号匹配到的文本,可以使用“$n”的形式来替换编号为n的匹配文本。这常用于String对象的replace()方法,例如交换等号两边的单词:


var reg = /(\w+)=(\w+)/;
var str = ‘love=hate';
str.replace(reg,'$2=$1');
//"hate=love"

接下来讨论匹配的顺序、贪婪与懒惰模式。

常见的重复匹配字符如?、+、*、{n,m}默认采用贪婪匹配,即尽可能多地匹配字符。与之相对的是懒惰匹配,即在重复字符后加上问号“?”,如??、+?、*?、{n,m}?,表示尽可能少地匹配。例如:

贪婪与懒惰匹配机制


var str = 'goooogle‘;
var reg1 = /o+/;   //"goooo"
var reg2 = /o+?/;  //"go"

对例子稍作修改后:


var str = 'goooogle‘;
var reg1 = /o+gle/;  //"oooogle"
var reg2 = /o+?gle/;  //"oooogle"

修改后的例子结果看似相同,但“/o+?gle/”并未直接匹配到“ogle”。这是因为正则表达式始终从左向右进行匹配,不会从右侧获取子串进行匹配。

虽然最终结果相同,但匹配原理不同。在reg1中,“o+”首先匹配所有的“o”,接着匹配“gle”,完成整体匹配。而在reg2中,“o+?”先匹配一个“o”,随后“gle”在字符串的第2位到第4位(即原串的“ooo”部分)匹配失败。引擎回溯至“o+?”,尝试匹配第二个“o”,成功后再在第3位到第4位匹配“gle”,以此类推,直到匹配整个字符串。

总体而言,必须记住:从左往右的匹配顺序优先级高于贪婪或懒惰匹配的选择。

零宽断言用于指定匹配条件而不消耗字符。

关于零宽断言的总体解释可参考博文《正则表达式30分钟入门教程》,值得注意的是,JS里只支持零宽先行断言。即零宽正预测先行断言"(?=exp)"和零宽负预测先行断言“(?!exp)”。 所谓“零宽”,就是它并不在匹配的结果字符中占据空间。例如“\w","\s"就会占据一个或几个空间,依匹配的字符长度决定。而像”^","$"这种对应的首末位置,不占据空间,零宽就是属于这一类。

所谓“正/负预测”,是指断言中要求满足或不满足的条件。“正”表示必须满足exp,“负”表示必须不满足exp。

所谓“先行”,是指被检查的字符串位于断言之前,零宽断言位于之后。即判断字符串的后一部分是否满足断言条件。

所谓“断言”,就是判断的条件本身。

以下是两种零宽断言的例子:

零宽先行断言详解


var str = 'java coffeescript';
var reg1 = /\b\w+(?=script\b)/; //coffee 
var reg2 = /\b\w+(?!script\b)/; //java

reg1为零宽正预测先行断言,“(?=script\b)”表示单词必须以“script”结尾,这是一种条件判断,不占用任何空间。

同样,reg2为零宽负预测先行断言,“(?!script\b)”表示单词不以“script”结尾。

另外,由于JavaScript不支持零宽后顾断言,无法直接判断字符串前面部分是否满足条件。但在JS中可以通过多次正则表达式实现:先匹配目标串,然后截取从开头到index的字符子串,再判断子串末尾是否符合所需的断言条件。具体用法可自行尝试。

match()与exec()方法的异同。

match和exec是正则表达式匹配字符串的常用方法,两者功能相似,但存在一些细微区别。

1、使用方式不同。

match是字符串包装对象的方法,用法为:String.match(RegExp); exec是正则表达式对象的方法,用法为:RegExp.exec(String);

2、返回的结果不同。

当RegExp未设置全局标志“g”时:

两者的返回结果相同。如果没有匹配值,返回null;如果有匹配值,返回一个数组(记为array)。array[0]为匹配的字符串,array[1]、array[2]……对应正则表达式中圆括号匹配的子字符串$1、$2……。同时,数组带有两个属性:array.index表示匹配字符串的初始位置,array.input表示正在检索的原始字符串。

当RegExp设置了全局标志“g”时:

match在有匹配值时返回一个数组array,数组的每一项依次表示所有匹配到的字符串,此时不再包含圆括号匹配的子字符串。此外,此时的数组没有index属性和input属性。

exec则与没有全局标志“g”时的表现无异。此时返回的是数组array,array[0]为当前匹配的字符串,array[1]、array[2]……为当前匹配下的圆括号匹配子串。此时需要注意RegExp对象的lastIndex属性,它表示原字符串中当前匹配字符串末尾的下一个位置。当没有进一步匹配结果时,lastIndex属性置为0。因此,可以利用lastIndex属性的循环来找出所有匹配字符串。例如:

match与exec方法的差异对比


var str = 'I love1 my job22';
var reg = /\b[a-z]+(\d+)\b/g;
array = str.match(reg);
//array = ["love1", "job22"] 
//array.index = undefind
//array.input = undefined
------------------------------------
array = reg.exec(str);
//array = ["love1", "1"]
//array.index = 2
//array.input = "I love1 my job22"
//reg.lastIndex = 7
//run again
reg.exec(str);
//array = ["job22", "22"]
//array.index = 11
//array.input = "I love1 my job22"
//reg.lastIndex = 16
//run again
reg.exec(str);
//reg.lastIndex = 0

最后,考虑到ECMAScript 3和ECMAScript 5的版本差异,每次匹配完成后记得手动将RegExp对象的lastIndex属性置0,以满足老版本非IE浏览器的要求。

js正则表达式,pattern,注意事项

简而言之,请使用“/\w+@+\w+(\.+\w+){1,}/.test(str)”来验证,而不要使用“"\w+@+\w+(\.+\w+){1,}".test(str)”来验证; 使用后者,直接输入“add@dfddf”即可通过验证(这通常是不期望的行为)。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章