JavaScript 正则表达式全方位解析:从基础到实战
正则表达式(Regular Expression,简称 Regex)是处理字符串的强大工具,它通过特定语法规则匹配、查找、替换字符串中的目标内容,广泛应用于表单验证、数据提取、格式清洗等场景。
一、正则表达式基础概念
1. 核心定义
正则表达式是描述字符串匹配模式的字符序列,本质是 “规则模板”,用于:
- 验证字符串格式(如手机号、邮箱、身份证号);
- 提取字符串中的目标内容(如从 URL 中提取参数);
- 替换字符串中的特定字符(如敏感词过滤)。
2. 正则表达式的特点
- 灵活性:一行正则可替代数十行字符串判断代码;
- 高效性:浏览器原生支持,匹配效率高于手动字符串遍历;
- 通用性:语法在 JavaScript、Java、Python 等语言中高度一致,学会后可跨语言复用。
二、正则表达式的两种创建方式
JavaScript 中创建正则表达式有两种方式:字面量模式和构造函数模式,适用于不同场景。
1. 字面量模式(推荐,简单场景)
用/正则内容/修饰符表示,直接嵌入代码,无需转义过多字符。
代码示例
javascript
// 1. 匹配字符串中的 "hello"(无修饰符)
const reg1 = /hello/;
console.log(reg1); // /hello/// 2. 匹配 "hello",忽略大小写(i 修饰符)
const reg2 = /hello/i;
console.log(reg2); // /hello/i// 3. 全局匹配 "hello"(g 修饰符)
const reg3 = /hello/g;
console.log(reg3); // /hello/g// 4. 多修饰符组合(全局匹配+忽略大小写)
const reg4 = /hello/gi;
console.log(reg4); // /hello/gi
注意事项
- 字面量中不能使用变量,若需动态拼接正则,需用构造函数模式;
- 特殊字符(如
/、\)需转义,例如匹配/需写为\/。
2. 构造函数模式(动态场景)
通过new RegExp(正则字符串, 修饰符)创建,支持动态传入正则规则(如变量拼接)。
代码示例
javascript
// 1. 基础构造(匹配 "hello",无修饰符)
const reg1 = new RegExp("hello");
console.log(reg1); // /hello/// 2. 带修饰符(全局匹配+忽略大小写)
const reg2 = new RegExp("hello", "gi");
console.log(reg2); // /hello/gi// 3. 动态拼接正则(例如从变量获取匹配内容)
const matchStr = "hello";
const reg3 = new RegExp(matchStr, "i"); // 动态传入匹配字符串
console.log(reg3.test("Hello World")); // true(忽略大小写匹配)// 4. 特殊字符转义(构造函数中需双重转义,因为字符串本身需转义)
const reg4 = new RegExp("\\d+"); // 匹配数字(等同于 /\d+/)
console.log(reg4.test("123")); // true
核心区别
| 特性 | 字面量模式 | 构造函数模式 |
|---|---|---|
| 语法格式 | /pattern/flags | new RegExp(pattern, flags) |
| 动态拼接 | 不支持 | 支持(参数可传变量) |
| 特殊字符转义 | 单重转义(如\/) | 双重转义(如\\/) |
| 性能 | 稍高(解析时编译) | 稍低(运行时编译) |
三、正则表达式核心语法(必掌握)
正则的强大之处在于其灵活的语法规则,分为字符分类、边界符、量词、分组、前瞻断言五大类,以下是高频语法的详细讲解。
1. 字符分类:匹配特定类型的字符
用于匹配某一类字符(如数字、字母、空格),是正则的基础组成部分。
| 语法 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
[abc] | 匹配 a、b、c 中的任意一个字符 | /[abc]/ | 匹配 "a"、"b"、"c" |
[a-z] | 匹配小写字母 a-z | /[a-z]/ | 匹配 "x"、"y"(小写字母) |
[A-Z] | 匹配大写字母 A-Z | /[A-Z]/ | 匹配 "X"、"Y"(大写字母) |
[0-9] | 匹配数字 0-9 | /[0-9]/ | 匹配 "1"、"2"(数字) |
\d | 等同于 [0-9](数字) | /\d/ | 匹配 "5"、"6" |
\D | 等同于 [^0-9](非数字) | /\D/ | 匹配 "a"、"!"(非数字) |
\w | 匹配字母、数字、下划线([a-zA-Z0-9_]) | /\w/ | 匹配 "a"、"5"、"_" |
\W | 匹配非字母、数字、下划线 | /\W/ | 匹配 "!"、"@"、" " |
\s | 匹配空白字符(空格、制表符、换行) | /\s/ | 匹配 ""、"\t"、"\n" |
\S | 匹配非空白字符 | /\S/ | 匹配 "a"、"1"、"!" |
. | 匹配除换行符(\n)外的任意字符 | /./ | 匹配 "a"、"1"、"!" |
代码示例
javascript
const str = "Hello 123!_";// 1. 匹配数字(\d)
const reg1 = /\d/;
console.log(reg1.test(str)); // true(字符串含 "123")// 2. 匹配非数字(\D)
const reg2 = /\D/;
console.log(reg2.test(str)); // true(字符串含 "Hello"、"!"、"_")// 3. 匹配空白字符(\s)
const reg3 = /\s/;
console.log(reg3.test(str)); // true("Hello" 和 "123" 之间有空格)// 4. 匹配任意字符(.)
const reg4 = /./;
console.log(reg4.test(str)); // true(匹配任意非换行字符)
2. 边界符:限定匹配的位置
用于限定目标字符在字符串中的位置(如开头、结尾、单词边界),避免部分匹配。
| 语法 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
^ | 匹配字符串开头 | /^hello/ | 匹配 "helloWorld"(开头是 hello) |
$ | 匹配字符串结尾 | /world$/ | 匹配 "helloWorld"(结尾是 world) |
\b | 匹配单词边界(单词与非单词之间) | /\bhello\b/ | 匹配 "hello world"(独立的 hello) |
\B | 匹配非单词边界 | /\Bhello\B/ | 匹配 "ahellob"(hello 前后非单词边界) |
代码示例
javascript
const str1 = "hello world";
const str2 = "helloworld";// 1. 匹配开头为 "hello"(^)
const reg1 = /^hello/;
console.log(reg1.test(str1)); // true(str1 开头是 hello)
console.log(reg1.test(str2)); // true(str2 开头是 hello)// 2. 匹配结尾为 "world"($)
const reg2 = /world$/;
console.log(reg2.test(str1)); // true(str1 结尾是 world)
console.log(reg2.test(str2)); // true(str2 结尾是 world)// 3. 匹配独立的 "hello"(\b)
const reg3 = /\bhello\b/;
console.log(reg3.test(str1)); // true(str1 中 hello 是独立单词)
console.log(reg3.test(str2)); // false(str2 中 hello 与 world 连在一起,无单词边界)
关键实战:精确匹配
用 ^ 和 $ 组合实现 “精确匹配”(字符串必须完全符合正则规则),常用于表单验证:
javascript
// 精确匹配 6 位数字(如验证码)
const reg = /^\d{6}$/;
console.log(reg.test("123456")); // true(完全匹配 6 位数字)
console.log(reg.test("12345")); // false(不足 6 位)
console.log(reg.test("12345a")); // false(含非数字字符)
3. 量词:限定字符出现的次数
用于控制目标字符的出现次数(如 “至少 1 次”“最多 3 次”“恰好 2 次”),是正则简化代码的核心。
| 语法 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
* | 匹配前面的字符 0 次或多次 | /a*/ | 匹配 ""、"a"、"aa" |
+ | 匹配前面的字符 1 次或多次 | /a+/ | 匹配 "a"、"aa"(不匹配 "") |
? | 匹配前面的字符 0 次或 1 次 | /a?/ | 匹配 ""、"a"(不匹配"aa") |
{n} | 匹配前面的字符恰好 n 次 | /a{2}/ | 匹配 "aa"(不匹配 "a"、"aaa") |
{n,} | 匹配前面的字符至少 n 次 | /a{2,}/ | 匹配 "aa"、"aaa" |
{n,m} | 匹配前面的字符 n 到 m 次 | /a{2,3}/ | 匹配 "aa"、"aaa"(不匹配 "a"、"aaaa") |
代码示例
javascript
const str = "aaabbb";// 1. 匹配 a 至少 2 次(a{2,})
const reg1 = /a{2,}/;
console.log(reg1.test(str)); // true(str 含 "aaa",满足至少 2 次)// 2. 匹配 b 恰好 3 次(b{3})
const reg2 = /b{3}/;
console.log(reg2.test(str)); // true(str 含 "bbb",恰好 3 次)// 3. 匹配 a 0 次或 1 次(a?)
const reg3 = /a?/;
console.log(reg3.test("")); // true(0 次)
console.log(reg3.test("a")); // true(1 次)
console.log(reg3.test("aa"));// true(但仅匹配第一个 a)// 4. 匹配 a 0 次或多次(a*)
const reg4 = /a*/;
console.log(reg4.test("")); // true(0 次)
console.log(reg4.test("aaa"));// true(3 次)
关键实战:贪婪与非贪婪模式
量词默认是贪婪模式(尽可能多匹配),在量词后加 ? 可变为非贪婪模式(尽可能少匹配):
javascript
const str = "aaaaa";// 贪婪模式(匹配最多 3 个 a)
const reg1 = /a{2,3}/;
console.log(str.match(reg1)); // ["aaa"](贪婪匹配,取 3 个 a)// 非贪婪模式(匹配最少 2 个 a)
const reg2 = /a{2,3}?/;
console.log(str.match(reg2)); // ["aa"](非贪婪匹配,取 2 个 a)
4. 分组与引用:批量匹配与复用
用 () 实现分组,将多个字符视为一个整体,支持批量匹配和结果引用。
4.1 分组匹配(批量控制)
javascript
// 匹配 "ab" 重复 2 次(分组后用 {2} 控制)
const reg = /(ab){2}/;
console.log(reg.test("abab")); // true("ab" 重复 2 次)
console.log(reg.test("ab")); // false(仅 1 次)
4.2 分组引用(复用匹配结果)
用 \n(n 为分组序号)引用之前分组的匹配结果,常用于匹配重复内容:
javascript
// 匹配前后相同的单词(如 "hello hello")
const reg = /(\w+)\s+\1/;
console.log(reg.test("hello hello")); // true(\1 引用第一个分组的 "hello")
console.log(reg.test("hello world")); // false(前后单词不同)// 提取分组结果(通过 exec 或 match 获取)
const str = "hello hello";
const result = reg.exec(str);
console.log(result[1]); // "hello"(第一个分组的结果)
4.3 选择符(或逻辑)
用 | 实现 “或” 逻辑,匹配多个模式中的任意一个:
javascript
// 匹配 "hello" 或 "world"
const reg = /hello|world/;
console.log(reg.test("hello")); // true
console.log(reg.test("world")); // true
console.log(reg.test("hi")); // false
5. 前瞻断言:匹配的 “附加条件”
前瞻断言用于判断目标字符 “后面是否满足某个条件”,不消耗字符本身,仅作为匹配的附加规则。
| 语法 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
(?=exp) | 正向前瞻:后面必须满足 exp | /a(?=b)/ | 匹配 "ab" 中的 "a"(后面是 b) |
(?!exp) | 负向前瞻:后面必须不满足 exp | /a(?!b)/ | 匹配 "ac" 中的 "a"(后面不是 b) |
代码示例
javascript
const str = "ab ac ad";// 1. 正向前瞻:匹配后面是 "b" 的 "a"
const reg1 = /a(?=b)/g;
console.log(str.match(reg1)); // ["a"](仅匹配 "ab" 中的 "a")// 2. 负向前瞻:匹配后面不是 "b" 的 "a"
const reg2 = /a(?!b)/g;
console.log(str.match(reg2)); // ["a", "a"](匹配 "ac"、"ad" 中的 "a")
实战场景:密码强度验证
用前瞻断言实现 “密码必须包含大小写字母、数字、特殊字符”:
javascript
// 密码强度验证:8-16位,含大小写字母、数字、特殊字符(!@#$%^&*)
const reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[a-zA-Z\d!@#$%^&*]{8,16}$/;
console.log(reg.test("Abc123!@#")); // true(满足所有条件)
console.log(reg.test("abc123!@#")); // false(无大写字母)
console.log(reg.test("Abcdefgh")); // false(无数字和特殊字符)
四、正则表达式实例方法(核心操作)
正则表达式对象提供了 4 个常用实例方法,用于匹配、验证、转换字符串,其中 test 和 exec 是高频方法。
1. test():验证是否匹配(返回布尔值)
判断字符串中是否存在匹配正则的内容,返回 true(存在)或 false(不存在),常用于表单验证。
代码示例
javascript
// 验证手机号(11位数字,以 13/14/15/17/18 开头)
const phoneReg = /^1[34578]\d{9}$/;
const phone1 = "13812345678";
const phone2 = "12345678901";console.log(phoneReg.test(phone1)); // true(符合手机号规则)
console.log(phoneReg.test(phone2)); // false(开头不是 13/14/15/17/18)
2. exec():获取匹配详情(返回数组或 null)
返回字符串中匹配正则的详细信息(如匹配内容、位置),若无匹配则返回 null。
代码示例
javascript
const str = "Hello World Hello JavaScript";
const reg = /Hello/g; // 全局匹配// 第一次执行 exec
let result1 = reg.exec(str);
console.log(result1);
// 输出:["Hello", index: 0, input: "Hello World Hello JavaScript", groups: undefined]// 第二次执行 exec(全局匹配,从 lastIndex 继续)
let result2 = reg.exec(str);
console.log(result2);
// 输出:["Hello", index: 12, input: "Hello World Hello JavaScript", groups: undefined]// 第三次执行 exec(无更多匹配,返回 null)
let result3 = reg.exec(str);
console.log(result3); // null
关键注意点
- 若正则有
g(全局)修饰符,exec会维护lastIndex属性,记录下一次匹配的起始位置; - 若正则无
g修饰符,每次exec都会从字符串开头重新匹配。
3. toString()/toLocaleString():转为字面量字符串
将正则对象转为字面量形式的字符串(如 /hello/),两者在 JavaScript 中功能一致。
代码示例
javascript
const reg = /hello/gi;
console.log(reg.toString()); // "/hello/gi"
console.log(reg.toLocaleString()); // "/hello/gi"
4. valueOf():返回正则本身
返回正则对象的原始值(即正则本身),一般用于确认正则的引用。
代码示例
javascript
const reg = /hello/;
console.log(reg.valueOf()); // /hello/
console.log(reg.valueOf() === reg); // true
五、String 对象的正则方法(字符串视角)
除了正则实例方法,String 对象也提供了 4 个支持正则的方法,从字符串视角操作正则。
1. match():获取所有匹配结果
返回字符串中所有匹配正则的内容,若正则有 g 修饰符则返回数组,否则返回详细匹配信息。
代码示例
javascript
const str = "Hello World Hello JavaScript";// 1. 无 g 修饰符(返回详细信息)
const reg1 = /Hello/;
console.log(str.match(reg1));
// 输出:["Hello", index: 0, input: "Hello World Hello JavaScript", groups: undefined]// 2. 有 g 修饰符(返回所有匹配结果)
const reg2 = /Hello/g;
console.log(str.match(reg2)); // ["Hello", "Hello"]
2. search():获取首次匹配位置
返回字符串中首次匹配正则的起始索引,若无匹配则返回 -1,忽略 g 修饰符。
代码示例
javascript
const str = "Hello World Hello JavaScript";
const reg = /World/;
console.log(str.search(reg)); // 6("World" 从索引 6 开始)
console.log(str.search(/Java/)); // 17("Java" 从索引 17 开始)
console.log(str.search(/Hi/)); // -1(无匹配)
3. replace():替换匹配内容
替换字符串中匹配正则的内容,返回新字符串(不修改原字符串),支持全局替换。
代码示例
javascript
运行
const str = "Hello World Hello JavaScript";// 1. 替换第一个 "Hello" 为 "Hi"(无 g 修饰符)
const reg1 = /Hello/;
console.log(str.replace(reg1, "Hi")); // "Hi World Hello JavaScript"// 2. 全局替换 "Hello" 为 "Hi"(有 g 修饰符)
const reg2 = /Hello/g;
console.log(str.replace(reg2, "Hi")); // "Hi World Hi JavaScript"// 3. 替换敏感词(* 替换)
const badWordReg = /敏感词/g;
console.log("这是敏感词,不能显示".replace(badWordReg, "***")); // "这是***,不能显示"
4. split():按正则分割字符串
按正则匹配的内容分割字符串,返回分割后的数组,支持复杂分割规则。
代码示例
javascript
运行
const str = "terry134briup156lisi12zhangsan";// 按 1 个或多个数字分割
const reg = /\d+/;
console.log(str.split(reg)); // ["terry", "briup", "lisi", "zhangsan"]// 按 "Hello" 或 "World" 分割
const str2 = "Hello123World456Hello789";
const reg2 = /Hello|World/;
console.log(str2.split(reg2)); // ["", "123", "456", "789"]
六、正则表达式实战案例(必看)
结合实际开发场景,用正则解决常见问题,巩固前面所学知识点。
1. 案例 1:验证邮箱格式
邮箱规则:包含 @ 和 .,@ 前为字母 / 数字 / 下划线,@ 后为域名(如 qq.com、gmail.com)。
javascript
const emailReg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
console.log(emailReg.test("123@qq.com")); // true
console.log(emailReg.test("abc-123@gmail.com")); // true
console.log(emailReg.test("abc@.com")); // false(@ 后无域名)
console.log(emailReg.test("abc@com")); // false(无 . 分隔域名)
2. 案例 2:提取 URL 中的参数
从 URL(如 https://www.baidu.com?name=tom&age=18)中提取参数,返回参数对象。
javascript
function getUrlParams(url) {const params = {};// 匹配 ? 后的参数(如 name=tom&age=18)const reg = /\?([^#]+)/;const paramStr = url.match(reg)[1]; // "name=tom&age=18"// 分割参数为数组(如 ["name=tom", "age=18"])const paramArr = paramStr.split("&");// 遍历数组,提取键值对paramArr.forEach(item => {const [key, value] = item.split("=");params[key] = decodeURIComponent(value); // 解码(处理中文等特殊字符)});return params;
}const url = "https://www.baidu.com?name=tom&age=18&city=%E5%8C%97%E4%BA%AC";
console.log(getUrlParams(url));
// 输出:{ name: "tom", age: "18", city: "北京" }
3. 案例 3:格式化手机号(中间 4 位打码)
将手机号 13812345678 格式化为 138****5678。
javascript
const phoneReg = /(\d{3})\d{4}(\d{4})/;
const phone = "13812345678";
const formattedPhone = phone.replace(phoneReg, "$1****$2");
console.log(formattedPhone); // "138****5678"
七、正则表达式避坑指南
- 特殊字符转义:正则中的特殊字符(如
.、*、?)需用\转义,例如匹配.需写为\.; - 全局匹配的
lastIndex问题:用exec或test时,若正则有g修饰符,多次调用会从lastIndex继续,如需重新匹配,需手动重置lastIndex = 0; - 贪婪模式的意外匹配:量词默认贪婪,如需最小匹配,需在量词后加
?(如{2,3}?); - 换行符的匹配:
.不匹配换行符(\n),如需匹配所有字符,需用[\s\S]或[\d\D]。
总结
正则表达式是 JavaScript 中处理字符串的 “瑞士军刀”,掌握其语法规则和方法后,能极大提升字符串处理效率。本文从基础到实战,覆盖了正则的核心知识点,建议重点掌握:
- 常用语法:
\d(数字)、{n,m}(量词)、^$(边界)、()(分组); - 高频方法:
test(验证)、exec(详情)、replace(替换); - 实战场景:表单验证、数据提取、格式清洗。
