我是在一个不算太正式的场合重新正视正则表达式的:当时有个刚入职不久的同事,在表单校验里为了判断手机号,写了一段十几次 indexOf 加 substring 的代码。代码能跑,但谁看了都头疼。我跟他说,你花十分钟把正则表达式基础过一遍,这个问题三行就能解决。半小时后他回来,第一句话是:这种模式描述也太好用了,为什么我之前一直躲着它。
这个场景在前端开发里太常见了。很多人把正则当成一门晦涩难懂的语言,觉得面试八股才需要背,日常开发能复制粘贴就复制粘贴。但实际上,正则表达式就是一套“描述字符串形状”的语法。只要你输入的是一段文本,需要判断它是否合法、从中提取信息、或者批量替换内容,正则都是前端工具箱里性价比最高的一件工具。这篇文章不搞学院派那套,也不追求把正则的所有边角语法堆给你,而是站在前端日常开发和面试实战的角度,把正则拆成一个能直接上手的知识体系:它到底在解决什么问题、必会的语法有哪些、手机号邮箱密码这些高频写法怎么推导出来、以及那些让老手都翻过车的坑。
如果你正在学前端、准备跳槽面试、或者写表单校验已经复制粘贴到怕了,这篇内容值得你花十五分钟读一遍,最好跟着敲一遍。
1. 真正理解正则:它解决的是“怎样描述一类字符串”的问题
1.1 正则不是一门语言,而是一种匹配思维
很多人第一次接触正则,会被 ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$ 这种长串吓到,下意识觉得这是一门外语。其实它的运行逻辑极其朴素:你给出一个“模式”,引擎拿着这个模式去目标字符串里做匹配,回答两个问题——“符合条件吗”和“哪里符合”。
这和你在地址簿里找人是一个道理。你说“找姓张、名字两个字的”,人脑会自动处理模糊匹配;但对计算机来说,你必须给它一套精确规则。^张\w$ 就是这套规则:以“张”开头,后面跟一个任意“字类”字符,然后结束。它不关心具体是“张伟”还是“张强”,它关心的是形状。
前端开发者尤其需要建立这种思维,因为前端处理的东西本质上都是字符串:接口返回的 JSON、用户输入的手机号、URL 上的 query、textarea 里的整段文本。你多掌握一点字符串形状的描述能力,就少写一堆垃圾判断代码。
1.2 前端三点一线:匹配、提取、替换
正则的能力,归纳起来就三件事:
- 匹配:判断一个字符串是否符合规则,比如校验手机号、邮箱;
- 提取:从一段杂乱文本里抓出符合规则的片段,比如从长文本里抓出所有手机号;
- 替换:把符合规则的片段替换成别的内容,比如把用户输入的连续空格统一成单个空格、脱敏手机号中间四位。
这三个需求对应到 JS 的 API,分别是 RegExp.prototype.test()、String.prototype.match() / RegExp.prototype.exec()、String.prototype.replace()。很多刚入门的朋友搞不清这些 API 的差别,本质上就是没想明白自己到底要做“匹配”还是“提取”还是“替换”。
举一个我正在用的例子:运营传了一个包含大量联系方式的 CSV 片段,需要前端展示前给手机号脱敏。原始文本可能是:
code复制张三 13812345678 李四 13911112222
要提取手机号,用 /\b1[3-9]\d{9}\b/g 去 match;要脱敏,就在 replace 里配合捕获分组写成:
javascript复制text.replace(/\b(1[3-9]\d)\d{4}(\d{4})\b/g, '$1****$2')
懂了这个三分法,几乎所有的正则使用场景都能归位。
1.3 从需求到正则的翻译模型
我在带人写正则时,总结了一个特别管用的“翻译模型”:先把中文需求拆成三个要素。
- 固定部分:必须出现的字面字符,比如手机号必须是数字1开头;
- 可变部分:某一段长度不确定、字符类型不确定,比如手机号后面可以跟9位数字;
- 边界部分:匹配到哪里结束,避免把“1381234567890 秒杀价”里的手机号误判成 13 位。
拿“13 位数字手机号码正则表达式”这个很多人搜过的需求来说。你要的不是“13 位数字”,而是“以 1 开头,第二位在 3-9 之间,后面跟 9 位数字”的 11 位号码。如果只写 ^\d{13}$,用户输入任意 13 位数字都会通过,那校验等于没做。这就是翻译需求时最常见的错误:只看到了“数字”和“位数”,没看到号码本身的构成规则。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 前端必会的正则语法地图:从字符、量词到断言
这一节我用的是“地图”思路,不会事无巨细地把所有元字符都讲一遍,而是按前端实际使用频率,把语法分成四个区块。每块记住几个关键点,其余用到查表就好。
2.1 字符匹配:字面量、字符类与简写
正则最基本的能力是匹配单个字符。a 就是匹配字母 a,5 就是匹配数字 5,这是字面量匹配,没有技术含量。真正的威力来自字符类(character class),它用方括号表示“这一位允许出现哪些字符”。
前端最常用的一组字符类我整理成了这张表:
| 写法 | 含义 | 等价类 | 典型场景 |
|---|---|---|---|
[0-9] |
任意数字 | \d |
手机号、金额 |
[a-zA-Z] |
任意字母 | 无简写,等价 \d 之于数字 |
用户名、验证码 |
[a-zA-Z0-9_] |
字母数字下划线 | \w |
变量名、单词 |
\s |
空白:空格、Tab、换行 | [\t\n\r\f ] |
去除空白、分词 |
. |
除换行外的任意字符 | 无 | 兜底匹配、日志解析 |
[^...] |
排除字符类 | 无 | 禁止特殊字符 |
使用 `\
