JavaScript正则表达式

1. 概览介绍

正则表达式(Regular Expression,简称 regex 或 regexp)是一种强大的文本模式匹配工具,在 JavaScript 中用于:

  • 验证字符串格式(如邮箱、手机号)
  • 查找和提取特定内容
  • 替换文本中的特定部分
  • 分割字符串

在 JavaScript 中,正则表达式可以通过两种方式创建:

  • 字面量语法/pattern/flags
  • RegExp 构造函数new RegExp("pattern", "flags")

2. 创建方式

2.1 字面量语法

javascript
// 匹配字符串中的 "hello",不区分大小写
const regex = /hello/i;
console.log(regex.test("Hello World")); // true

2.2 构造函数

javascript
// 动态创建正则表达式
const pattern = "hello";
const flags = "i";
const regex = new RegExp(pattern, flags);
console.log(regex.test("Hello World")); // true

2.3 两种方式的区别

特性 字面量语法 RegExp 构造函数
写法 /pattern/flags new RegExp("pattern", "flags")
模式类型 静态(编译时确定) 动态(运行时确定)
特殊字符 需直接转义 需双重转义
适用场景 模式固定时 模式需要动态生成时

3. 标志(Flags)

正则表达式可以使用以下标志来修改匹配行为:

标志 描述 示例
g 全局匹配,找到所有匹配项 /a/g 匹配 “abcabc” 中的所有 “a”
i 不区分大小写 /A/i 匹配 “a” 和 “A”
m 多行模式,^$ 匹配每行的开头和结尾 /^a/m 匹配 “abc\ndef” 中的 “a”
s 点号匹配换行符(ES9+) /a.b/s 匹配 “a\nb”
u Unicode 模式,正确处理 Unicode 字符 /\u{1F600}/u 匹配笑脸表情 😀
y 粘性匹配,从 lastIndex 开始匹配 const regex = /a/y; regex.lastIndex = 1; regex.test("abc") 返回 true

4. 正则表达式模式语法

4.1 字符匹配

普通字符

普通字符直接匹配自身,如 a 匹配字符串中的 “a”。

转义字符

使用反斜杠 \ 转义特殊字符:

  • \n:换行符
  • \t:制表符
  • \.:匹配点号本身
  • \\:匹配反斜杠本身

字符集合

  • [abc]:匹配 “a”、“b” 或 “c”
  • [a-z]:匹配任意小写字母
  • [A-Z]:匹配任意大写字母
  • [0-9]:匹配任意数字
  • [^abc]:匹配除 “a”、“b”、“c” 之外的任意字符

4.2 预定义字符类

字符 描述 等价于
. 匹配除换行符外的任意字符 [^\n\r]
\d 匹配任意数字 [0-9]
\D 匹配非数字 [^0-9]
\w 匹配字母、数字或下划线 [a-zA-Z0-9_]
\W 匹配非字母、数字或下划线 [^a-zA-Z0-9_]
\s 匹配空白字符(空格、制表符、换行符等) [ \t\n\r\f\v]
\S 匹配非空白字符 [^ \t\n\r\f\v]

4.3 边界匹配

字符 描述
^ 匹配字符串或行的开头
$ 匹配字符串或行的结尾
\b 匹配单词边界(字母与非字母之间)
\B 匹配非单词边界

示例:

javascript
/^hello/.test("hello world"); // true,hello 在开头
/hello$/.test("say hello"); // true,hello 在结尾
/\bworld\b/.test("hello world"); // true,world 是完整单词
/\bworld\b/.test("helloworld"); // false,world 不是完整单词

4.4 量词

量词 描述 示例
* 匹配 0 次或多次 a* 匹配 “”, “a”, “aa” 等
+ 匹配 1 次或多次 a+ 匹配 “a”, “aa” 等,不匹配 “”
? 匹配 0 次或 1 次 a? 匹配 “” 或 “a”
{n} 匹配恰好 n 次 a{3} 仅匹配 “aaa”
{n,} 匹配至少 n 次 a{2,} 匹配 “aa”, “aaa” 等
{n,m} 匹配 n 到 m 次 a{1,3} 匹配 “a”, “aa”, “aaa”

贪婪模式与惰性模式

  • 贪婪模式:默认情况下,量词会尽可能匹配更多字符
  • 惰性模式:在量词后添加 ?,使其尽可能匹配更少字符
javascript
const str = "<div>content1</div><div>content2</div>";

// 贪婪模式:匹配从第一个 <div> 到最后一个 </div>
str.match(/<div>.*<\/div>/); // ["<div>content1</div><div>content2</div>"]

// 惰性模式:匹配第一个 <div> 和其对应的 </div>
str.match(/<div>.*?<\/div>/); // ["<div>content1</div>"]

4.5 分组与捕获

捕获组

使用括号 () 创建捕获组,捕获匹配的内容:

javascript
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const match = regex.exec("2023-10-05");
// match[0]: "2023-10-05"(完整匹配)
// match[1]: "2023"(第一个捕获组)
// match[2]: "10"(第二个捕获组)
// match[3]: "05"(第三个捕获组)

非捕获组

使用 (?:...) 创建非捕获组,仅用于分组,不捕获内容:

javascript
const regex = /(?:https?:\/\/)?(www\.[^\s]+)/;
const match = regex.exec("https://www.example.com");
// match[0]: "https://www.example.com"
// match[1]: "www.example.com"(只有一个捕获组)

命名捕获组(ES9+)

使用 (?<name>...) 为捕获组命名:

javascript
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec("2023-10-05");
// match.groups.year: "2023"
// match.groups.month: "10"
// match.groups.day: "05"

反向引用

使用 \1, \2 等引用之前的捕获组,或 \k<name> 引用命名捕获组:

javascript
// 匹配重复的单词
const regex = /\b(\w+)\s+\1\b/;
regex.test("hello hello"); // true

// 使用命名捕获组的反向引用
const regex2 = /(?<word>\w+)\s+\k<word>/;
regex2.test("hello hello"); // true

4.6 断言

断言用于检查匹配位置的前后内容,但不包含在匹配结果中。

先行断言

  • (?=...):正向先行断言,匹配后面跟着特定内容的位置
  • (?!...):负向先行断言,匹配后面不跟着特定内容的位置
javascript
// 匹配后面跟着 "@example.com" 的用户名
const regex = /\w+(?=@example\.com)/;
regex.exec("user@example.com"); // ["user"]

// 匹配后面不跟着数字的单词
const regex2 = /\w+(?!\d)/;
regex2.exec("hello123"); // ["hello"]

后行断言(ES9+)

  • (?<=...):正向后行断言,匹配前面跟着特定内容的位置
  • (?<!...):负向后行断言,匹配前面不跟着特定内容的位置
javascript
// 匹配前面是 "$" 的数字
const regex = /(?<=\$)\d+/;
regex.exec("Price: $100"); // ["100"]

// 匹配前面不是 "$" 的数字
const regex2 = /(?<!\$)\d+/;
regex2.exec("Price: 100"); // ["100"]

4.7 选择分支

使用 | 表示或关系:

javascript
// 匹配 "cat" 或 "dog"
const regex = /cat|dog/;
regex.test("I have a cat"); // true
regex.test("I have a dog"); // true

⚠️ 注意:选择分支是惰性的,匹配到第一个符合条件的分支就会停止。

5. RegExp 对象的方法与属性

5.1 方法

test(str)

检查字符串是否匹配正则表达式,返回布尔值:

javascript
const regex = /hello/;
regex.test("Hello World"); // false(区分大小写)
regex.test("hello world"); // true

exec(str)

查找匹配项,返回匹配结果数组或 null:

javascript
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const result = regex.exec("Today is 2023-10-05");
// result: ["2023-10-05", "2023", "10", "05", index: 9, input: "Today is 2023-10-05", groups: undefined]

5.2 属性

属性 描述 示例
flags 返回正则表达式的所有标志 /abc/gim.flags 返回 “gim”
global 是否启用全局匹配 /abc/g.global 返回 true
ignoreCase 是否忽略大小写 /abc/i.ignoreCase 返回 true
multiline 是否启用多行模式 /abc/m.multiline 返回 true
source 返回正则表达式的模式字符串 /abc/g.source 返回 “abc”
lastIndex 下一次匹配的起始位置(仅全局模式有效) /a/g.lastIndex

6. String 中与正则相关的方法

6.1 match()

查找匹配项,返回匹配结果数组或 null:

javascript
const str = "abcabc";
str.match(/a/); // ["a", index: 0, input: "abcabc", groups: undefined]
str.match(/a/g); // ["a", "a"](全局模式返回所有匹配项)

6.2 matchAll()(ES9+)

返回所有匹配项的迭代器:

javascript
const str = "abcabc";
const matches = [...str.matchAll(/a/g)];
// matches: [
//   ["a", index: 0, input: "abcabc", groups: undefined],
//   ["a", index: 3, input: "abcabc", groups: undefined]
// ]

6.3 search()

查找第一个匹配项的索引,返回索引或 -1:

javascript
const str = "abcabc";
str.search(/a/); // 0
str.search(/z/); // -1

6.4 replace()

替换匹配项:

javascript
const str = "hello world";
str.replace(/world/, "JavaScript"); // "hello JavaScript"

// 使用函数作为替换值
str.replace(/\w+/g, (match) => match.toUpperCase()); // "HELLO WORLD"

6.5 replaceAll()(ES9+)

替换所有匹配项(无需使用 g 标志):

javascript
const str = "abcabc";
str.replaceAll("a", "x"); // "xbcxbc"

6.6 split()

根据匹配项分割字符串:

javascript
const str = "a,b,c";
str.split(","); // ["a", "b", "c"]

// 使用正则表达式分割
const str2 = "a1b2c3";
str2.split(/\d+/); // ["a", "b", "c"]

7. 常用正则示例

7.1 邮箱验证

javascript
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
emailRegex.test("user@example.com"); // true
emailRegex.test("invalid-email"); // false

7.2 URL 提取

javascript
const urlRegex = /https?:\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/g;
const text = "Visit https://www.example.com and http://test.org";
text.match(urlRegex); // ["https://www.example.com", "http://test.org"]

7.3 手机号格式(中国大陆)

javascript
const phoneRegex = /^1[3-9]\d{9}$/;
phoneRegex.test("13812345678"); // true
phoneRegex.test("12345678901"); // false(开头不是13-19)

7.4 密码强度规则(至少8位,包含字母和数字)

javascript
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
passwordRegex.test("password123"); // true
passwordRegex.test("password"); // false(没有数字)
passwordRegex.test("12345678"); // false(没有字母)

7.5 提取 HTML 标签内容

javascript
const htmlRegex = /<([a-z]+)>(.*?)<\/\1>/;
const html = "<div>content</div>";
html.match(htmlRegex); // ["<div>content</div>", "div", "content"]

7.6 日期格式匹配(YYYY-MM-DD)

javascript
const dateRegex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
dateRegex.test("2023-10-05"); // true
dateRegex.test("2023-13-05"); // false(月份无效)
dateRegex.test("2023-10-32"); // false(日期无效)

8. 性能与最佳实践

8.1 避免灾难性回溯

复杂的嵌套量词可能导致灾难性回溯,影响性能:

javascript
// 不推荐:可能导致性能问题
const badRegex = /(a+)+$/;
const str = "aaaaa";
// badRegex.test(str); // 可能会卡住

// 推荐:简化正则表达式
const goodRegex = /a+$/;

8.2 预编译常用正则

对于重复使用的正则表达式,使用字面量语法预编译:

javascript
// 预编译正则表达式
const regex = /\w+/g;

// 多次使用
for (let i = 0; i < 1000; i++) {
  regex.test(`test${i}`);
}

8.3 使用非捕获组提升性能

如果不需要捕获匹配内容,使用非捕获组 (?:...)

javascript
// 不推荐:使用了不必要的捕获组
const badRegex = /(abc)+/;

// 推荐:使用非捕获组
const goodRegex = /(?:abc)+/;

8.4 注意 Unicode 字符处理

使用 u 标志正确处理 Unicode 字符:

javascript
// 匹配 Unicode 表情
const regex = /\u{1F600}/u;
regex.test("😀"); // true

8.5 优先使用简单模式

能用简单正则解决的问题,不要使用复杂正则:

javascript
// 不推荐:使用复杂正则匹配固定字符串
const badRegex = /^hello$/;

// 推荐:直接使用字符串比较
const str = "hello";
str === "hello";

9. 浏览器兼容性与 ES 新特性

特性 ES 版本 浏览器支持
命名捕获组 (?<name>...) ES9 (2018) Chrome 64+, Firefox 78+, Safari 11.1+
后行断言 (?<=...)(?<!...) ES9 (2018) Chrome 64+, Firefox 78+, Safari 12+
s 标志(dotAll) ES9 (2018) Chrome 62+, Firefox 78+, Safari 12+
matchAll() ES9 (2018) Chrome 63+, Firefox 67+, Safari 13+
replaceAll() ES11 (2020) Chrome 85+, Firefox 77+, Safari 13.1+

9.1 替代方案与 Polyfill

对于不支持新特性的浏览器,可以使用以下方法:

  1. 使用 Babel 转译新的正则语法
  2. 使用 polyfill 实现新方法(如 matchAllreplaceAll
  3. 手动实现 复杂功能(如后行断言)

10. 调试与工具推荐

10.1 在线测试工具

10.2 浏览器控制台调试

使用浏览器控制台调试正则表达式:

javascript
const regex = /(\d+)-(\w+)/;
const str = "123-abc";

// 使用 exec() 查看详细匹配信息
console.log(regex.exec(str));

// 使用 test() 快速验证
console.log(regex.test(str));

// 查看正则表达式的属性
console.log(regex.flags);
console.log(regex.source);

10.3 可视化工具

延伸阅读

最后更新:2026-02-05