正则表达式实战指南:从手机号验证到模板引擎,5 个案例彻底搞懂 RegExp
正则表达式是前端面试的高频考点,也是实际开发中处理字符串的利器。这篇文章通过 5 个真实案例,带你从零掌握正则的核心语法。
前言
今天课程从一道拼多多笔试题出发,系统学习了 JavaScript 正则表达式(RegExp)。
从手机号验证到字符串提取,从驼峰命名转换到简易模板引擎------每个案例都对应正则的一个核心用法。这篇文章将完整复盘学习过程。
一、正则表达式是什么?
1.1 一句话理解
正则表达式(Regular Expression)是一种描述字符串匹配规则的工具。它用一种特殊的语法,精确地定义"我要找什么样的字符串"。
1.2 核心语法速查
scss
正则表达式核心语法
/ / ← 正则的字面量写法
[] ← 字符范围,匹配括号内的任意一个字符
\d ← 数字(等价于 [0-9])
\w ← 字母/数字/下划线(等价于 [a-zA-Z0-9_])
{} ← 匹配次数
^ ← 以什么开头
$ ← 以什么结尾
g ← 全局匹配修饰符
() ← 分组(捕获)
$1 ← 引用第 1 个分组
1.3 JS 中的正则类型
javascript
console.log(typeof /abc/); // "object"
console.log(typeof {}); // "object"
// 如何区分正则和普通对象?
console.log(
Object.prototype.toString.call(/abc/)
); // "[object RegExp]"
💡
typeof无法区分正则和普通对象(都是object),需要用Object.prototype.toString.call()来精确判断。
二、案例一:手机号验证(拼多多笔试题)
2.1 需求分析
验证用户输入的手机号是否合法:
| 规则 | 说明 |
|---|---|
| 11 位数字 | 手机号固定 11 位 |
| 以 1 开头 | 中国手机号首位固定为 1 |
| 第二位 3-9 | 第二位不能为 0、1、2 |
| 后 9 位数字 | 剩余 9 位为任意数字 |
2.2 正则拆解
javascript
const reg = /^1[3-9]\d{9}$/;
css
正则逐字符解析
/^1[3-9]\d{9}$/
│ │ │ │ │ │
│ │ │ │ │ └── $ 字符串结尾(精确匹配)
│ │ │ │ └─── {9} 前面的 \d 重复 9 次
│ │ │ └────── \d 任意数字 [0-9]
│ │ └───────── [3-9] 第二位 3 到 9
│ └──────────── 1 第一位固定为 1
└─────────────── ^ 字符串开头(精确匹配)
2.3 完整代码
javascript
let str = '13888888888';
let reg = /^1[3-9]\d{9}$/;
console.log(reg.test(str)); // true
reg.test('12345678901'); // false(第二位是 2)
reg.test('1388888888'); // false(只有 10 位)
reg.test('23888888888'); // false(不以 1 开头)
🎯 永远不要相信用户的输入------把用户当小白,所有输入都要验证。
三、案例二:提取字符串中的数字
3.1 需求
从一段文本中提取所有数字。
3.2 核心语法:match + g 全局匹配
javascript
const str = '价格是100元,进价是90元,赚了10元';
const reg = /\d+/g;
const res = str.match(reg);
console.log(res); // ['100', '90', '10']
bash
正则解析
/\d+/g
│ │ └── g 全局匹配(不找到第一个就停,找所有)
│ └─── + 匹配一次或多次(贪婪模式,尽可能多匹配)
└────── \d 匹配任意数字
匹配过程:
'价格是100元,进价是90元,赚了10元'
^^^ ^^^ ^^
100 90 10
3.3 match vs test
| 方法 | 返回值 | 用途 |
|---|---|---|
reg.test(str) |
true / false |
判断是否匹配 |
str.match(reg) |
匹配结果的数组 | 提取匹配内容 |
💡
+量词 :\d只匹配一个数字,\d+匹配一个或多个数字。+是贪婪模式,会尽可能多地匹配。
四、案例三:字符串替换(驼峰命名转换)
4.1 需求
将 hello-world 转换为 helloWorld(短横线命名 → 小驼峰命名)。
4.2 核心语法:replace + () 分组
javascript
const str = 'hello-world';
const reg = /-(\w)/;
// match 查看分组捕获
console.log(str.match(reg));
// ['h-w', 'w', index: 5, ...]
// ↑ ↑
// 完整匹配 第1个分组(括号内的内容)
4.3 分组(Group)详解
scss
/-(\w)/
│ │
│ └── (\w) 分组:用括号包裹,捕获匹配的内容
└───── - 匹配连字符
match 返回值结构:
['-w', 'w', index: 5, input: 'hello-world']
↑ ↑
完整 第1个分组($1)
匹配 括号内的内容
4.4 replace 的回调函数
javascript
const str = 'hello-world';
const reg = /-(\w)/;
const res = str.replace(reg, (_, c) => {
console.log(_, c); // '-w' 'w'
// _ = 完整匹配 c = 第1个分组
return c.toUpperCase();
});
console.log(res); // 'helloWorld'
javascript
replace 回调函数参数
str.replace(reg, (完整匹配, 分组1, 分组2, ..., 偏移量, 原字符串) => {
// 完整匹配 = _(通常用 _ 表示不使用)
// 分组1 = $1
// 偏移量 = 匹配在原字符串中的位置
// 原字符串 = 原始输入
});
4.5 多个连字符的处理
javascript
// 只替换了第一个 -w
'hello-world'.replace(/-(\w)/, (_, c) => c.toUpperCase())
// 'helloWorld' ✅
// 如果有多个连字符?
'hello-my-world'.replace(/-(\w)/, (_, c) => c.toUpperCase())
// 'helloMy-world' ❌ 只替换了第一个!
📌 注意 :不加
g修饰符,replace只替换第一个匹配。要替换所有匹配,需要加g。
五、案例四:replace 回调函数深入理解
5.1 回调函数的参数结构
javascript
"hello-world".replace(
/-(\w)/,
(...args) => {
console.log(args);
// args[0] = '-w' 完整匹配
// args[1] = 'w' 第1个分组
// args[2] = 5 匹配位置的偏移量
// args[3] = 'hello-world' 原始字符串
return '';
}
);
5.2 参数对照表
| 参数位置 | 含义 | 示例值 |
|---|---|---|
args[0] / _ |
完整匹配的子串 | '-w' |
args[1] / $1 |
第 1 个分组 | 'w' |
args[2] / $2 |
第 2 个分组(如有) | --- |
args[n-2] |
偏移量(匹配位置) | 5 |
args[n-1] |
原始字符串 | 'hello-world' |
💡
_是约定俗成的写法,表示"这个参数我不关心,用下划线占位"。这是 JS 开发中的常见惯例。
六、案例五:简易模板引擎(递归 + 正则)
6.1 需求
实现一个简易模板引擎,将 {{name}} 替换为实际数据。
javascript
let template = `我是{{name}},今年{{age}}岁,性别{{sex}}`;
let person = {
name: '张三',
age: 18,
sex: '男'
};
6.2 核心思路
kotlin
模板引擎工作流程
"我是{{name}},今年{{age}}岁"
│
▼
找到第一个 {{name}}
│
▼
替换为 data['name'] → '张三'
│
▼
"我是张三,今年{{age}}岁"
│
▼
找到下一个 {{age}}
│
▼
替换为 data['age'] → 18
│
▼
"我是张三,今年18岁,性别{{sex}}"
│
▼
... 递归直到没有 {{}} 为止
6.3 完整代码
javascript
let template = `我是{{name}},今年{{age}}岁,性别{{sex}}`;
let person = {
name: '张三',
age: 18,
sex: '男'
};
function render(template, data) {
// {} 在正则中有特殊含义,需要转义为 \{\{
const reg = /\{\{(\w+)\}\}/;
if (reg.test(template)) {
// exec 返回匹配结果数组,[1] 是第一个分组
const name = reg.exec(template)[1];
// 用 data 中的值替换模板变量
template = template.replace(reg, data[name]);
// 递归:继续处理下一个 {{}}
return render(template, data);
}
return template;
}
console.log(render(template, person));
// "我是张三,今年18岁,性别男"
6.4 正则拆解
scss
/\{\{(\w+)\}\}/
│ │ │ │ │
│ │ │ │ └── } 匹配右花括号
│ │ │ └─── \} 转义的右花括号
│ │ └────── (\w+) 分组:捕获变量名
│ └───────── \{ 转义的左花括号
└──────────── \{ 匹配左花括号
为什么需要转义?
{} 在正则中表示"匹配次数",如 \d{3}
要匹配字面量的 {,需要写 \{
6.5 exec vs match
| 方法 | 调用者 | 返回值 |
|---|---|---|
reg.exec(str) |
正则对象 | 匹配详情数组(含分组) |
str.match(reg) |
字符串对象 | 匹配结果数组 |
javascript
const reg = /\{\{(\w+)\}\}/;
const template = '我是{{name}}';
reg.exec(template);
// ['{{name}}', 'name', index: 2, input: '我是{{name}}']
// ↑ 完整匹配 ↑ 分组1
template.match(reg);
// ['{{name}}', 'name', index: 2, input: '我是{{name}}']
// 结果相同(无 g 修饰符时)
🎯 这个简易模板引擎的原理,就是 Vue、React 等框架模板编译的基础思想!
七、JS 数据类型补充
7.1 两大类型阵营
javascript
JS 数据类型
基本类型(值类型)
├── Number 数值
├── String 字符串
├── Boolean 布尔值
├── Null 空值
└── Undefined 未定义
引用类型(对象类型)
├── Object 普通对象
├── Array 数组
├── Function 函数
├── Date 日期
├── RegExp 正则表达式 ← 今天的主角
├── Error 错误对象
├── Math 数学对象
└── JSON JSON 对象
💡 RegExp 是引用类型 ,
typeof返回object。它是 JS 中专门处理字符串模式匹配的对象。
八、知识图谱
scss
📚 正则表达式知识图谱
核心语法
├── / / 字面量
├── [] 字符范围
├── \d 数字
├── \w 字母/数字/下划线
├── {} 匹配次数
├── ^ / $ 开头 / 结尾
├── g 全局匹配修饰符
├── () 分组捕获
└── $1 引用分组
JS 正则方法
├── reg.test(str) 是否匹配 → boolean
├── str.match(reg) 提取匹配 → 数组
├── str.replace(reg, fn) 替换匹配 → 新字符串
└── reg.exec(str) 匹配详情 → 数组
实战案例
├── 手机号验证
│ └── /^1[3-9]\d{9}$/
├── 提取数字
│ └── /\d+/g + match
├── 驼峰命名转换
│ └── /-(\w)/ + replace 回调
└── 简易模板引擎
└── /\{\{(\w+)\}\}/ + 递归
关键概念
├── 分组 ():捕获匹配内容
├── $1:引用第 1 个分组
├── g 修饰符:全局匹配
├── + 量词:一次或多次
├── 转义 \{:匹配字面量 {
└── 递归:重复处理直到无匹配
九、正则速查表
| 语法 | 含义 | 示例 |
|---|---|---|
. |
任意字符(除换行) | /a.c/ → abc |
\d |
数字 [0-9] |
/\d+/ → 123 |
\w |
字母/数字/下划线 | /\w+/ → hello |
\s |
空白字符 | /\s+/ → |
^ |
开头 | /^hello/ |
$ |
结尾 | /world$/ |
* |
0 次或多次 | /ab*c/ → ac |
+ |
1 次或多次 | /ab+c/ → abc |
? |
0 次或 1 次 | /colou?r/ → color |
{n} |
恰好 n 次 | /\d{4}/ → 2024 |
{n,m} |
n 到 m 次 | /\d{1,3}/ → 42 |
[] |
字符范围 | /[a-z]/ → a |
() |
分组捕获 | /(ab)/ |
| ` | ` | 或 |
g |
全局匹配 | /\d+/g |
结语
正则表达式看似"天书",但掌握了核心语法后,你会发现它处理字符串的能力非常强大。
今天的 5 个案例覆盖了正则最常用的场景:
- 验证 :
test()+^$精确匹配 - 提取 :
match()+g全局匹配 - 替换 :
replace()+()分组 - 回调 :
replace(fn)+$1引用 - 递归 :
exec()+ 递归函数
记住:正则不是背出来的,是用出来的。遇到字符串处理的需求,先想想能不能用正则解决。
希望这篇文章对你有帮助!如果有任何问题,欢迎在评论区交流。
📌 参考资源
📌 文章标签
JavaScript正则表达式RegExp前端面试字符串处理学习笔记
觉得有收获?点个赞鼓励一下吧!有问题欢迎评论区留言~ 👍