
前端新人必看:JavaScript正则表达式从入门到实战(附避坑指南)
- 前端新人必看:JavaScript正则表达式从入门到实战(附避坑指南)
- 引言——为什么正则表达式总让人又爱又恨?
- 正则表达式到底是什么——把“找规律”翻译成“写公式”
- JavaScript 中创建正则的两种姿势——字面量爽一时,构造函数保一世
- 核心语法全解析——把“正则符号表”换成“人话说明书”
- RegExp 对象隐藏技能——别只会 `.test`
- 修饰符不只是可选项——“小字母,大差别”
- 正则性能那些事儿——“贪婪与懒惰的拉锯战”
- 表单验证中的经典用法——“一行正则行天下?别冲动”
- 文本处理实战技巧——“日志、敏感词、模板,一把梭”
- 遇到“不匹配”怎么办——“调试正则的六脉神剑”
- 写出可维护正则的秘诀——“命名、注释、模块化”
- 那些年踩过的正则大坑——“血与泪的 FAQ”
- 让正则更聪明的小技巧——“和现代 JS 打成一片”
- 结语——“正则不是银弹,却是瑞士军刀”
前端新人必看:JavaScript正则表达式从入门到实战(附避坑指南)
引言——为什么正则表达式总让人又爱又恨?
我第一次在代码里写下 /^[a-z]+$/i 的时候,内心是崩溃的:这串像猫踩键盘蹦出来的符号,居然就能拦住用户输入“!@#$%”?可当我发现一行 .replace(/(\d{4})(\d{4})(\d{4})(\d{4})/g,'$1 **** **** $4') 就能把银行卡号瞬间脱敏,瞬间又原地复活。正则就像前端界的香菜——有人闻到就皱眉,有人顿顿离不了。它能在三行代码里干掉你原本要写三十行的字符串校验,也能在凌晨三点因为一个回溯爆炸把你 CPU 飙到 100%。今天这篇,把我十年踩过的坑、哭过的泪、偷过的懒,全部打包成一篇“人话版”攻略,带你从“这是啥”到“这题我会”,再到“这坑我熟”。
正则表达式到底是什么——把“找规律”翻译成“写公式”
别被“正则”这俩字吓到,它本质上就是“用文字描述文字”的模板。想象你在公司群@人:
“请所有姓‘张’、名字两个字的小伙伴来领月饼。”
这句话翻译成正则,就是 /^张[\u4e00-\u9fa5]{2}$/。拆开看:
^—— 开头,表示“从姓开始”张—— 字面量,必须匹配“张”[\u4e00-\u9fa5]{2}—— 任意两个汉字(Unicode 范围)$—— 结尾,表示“名字只能两个字”
你看,正则只是把“口语规则”换成“符号规则”。学会这门“符号口语”后,你就能让浏览器帮你“一目十行”地找规律,而不是人肉 for 循环里写一堆 if。
JavaScript 中创建正则的两种姿势——字面量爽一时,构造函数保一世
1. 字面量写法——顺手拈来
// 校验十六进制颜色 #ff0012
const hex = /^#[0-9a-f]{6}$/i;
hex.test('#BADA55'); // true
优点:
- 写在斜杠里,像写字符串一样直观
- 性能略高(引擎提前编译)
缺点:
- 不能插变量!下面这种写法会原地爆炸
const color = 'red';
const reg = /^#color$/; // 想匹配 "#red"?别闹,它字面量就是 "#color"
2. 构造函数写法——动态拼接必备
// 用户输入的关键词高亮
function buildHighlightRegex(keyword) {
// 别忘了转义用户输入,否则“.”会被当成元字符
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(`(${escaped})`, 'gi');
}
const reg = buildHighlightRegex('(C++)');
'<title>C++ primer</title>'.replace(reg, '<mark>$1</mark>');
// "<mark>(C++)</mark> primer"
优点:
- 字符串模板里随便塞变量,动态构建真香
- 可以复用用户输入、后端配置
缺点:
- 写两层转义写到怀疑人生:字符串里先写一次“\”,RegExp 里再读一次“\”
- 运行时才编译,短生命周期场景性能略差
小结:静态规则用字面量,运行时规则用构造函数;别在循环里 new RegExp 一百万次,浏览器会喊你爸爸。
核心语法全解析——把“正则符号表”换成“人话说明书”
元字符——“不是你以为的那个意思”
| 符号 | 人话解释 | 代码示例 |
|---|---|---|
. | 除了换行啥都行 | /a.c/.test('a\nc') // false |
\d | digit = 数字 | /\d{11}/.test('13800138000') // true |
\w | word = 字母数字下划线 | /\w+/.test('var_1') // true |
\s | space = 空白 | /a\sb/.test('a b') // true |
量词——“几次”
| 符号 | 人话解释 | 代码示例 |
|---|---|---|
{n,m} | 最少 n 次,最多 m 次 | /\d{3,5}/.exec('12345')[0] // "12345" |
? | 0 或 1 次,等价 {0,1} | /colou?r/i.test('color') // true |
+ | 至少 1 次,等价 {1,} | /go+gle/i.test('goooogle') // true |
* | 任意次,包括 0 | /<[^>]*>/.exec('<div>hi</div>')[0] // "<div>" |
边界符——“别串戏”
// 匹配“web”单词,而不是“微博”里的‘wb’
const wordWeb = /\bweb\b/i;
wordWeb.test('web前端'); // true
wordWeb.test('微博'); // false
分组 + 反向引用——“自己抓到的自己用”
// 把 yyyy-mm-dd 换成 mm/dd/yyyy
const date = '2025-01-07';
const us = date.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1');
console.log(us); // "01/07/2025"
分组 $1、$2、$3 就是“反向引用”,把左边括号按出现顺序编号。
进阶:命名分组(ES2018)——再也不数 $1$2$3 数到眼瞎:
const named = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const m = '2025-01-07'.match(named);
console.log(m.groups.year); // "2025"
RegExp 对象隐藏技能——别只会 .test
1. test——“是或不是”
const mobile = /^1[3-9]\d{9}$/;
mobile.test('13800138000'); // true
注意:全局标志 g 会捣乱——lastIndex 会记忆状态,连续调用容易翻车:
const re = /a/g;
console.log(re.test('a')); // true
console.log(re.test('a')); // false ???
// 解决:别带 g,或者手动 re.lastIndex = 0
2. exec——“给我详情”
const re = /(?<proto>https?):\/\/(?<domain>[^/?#]+)/g;
let m;
while ((m = re.exec('https://kimi.com http://baidu.com')) !== null) {
console.log(m.groups.domain); // kimi.com baidu.com
}
3. String 家族方法——“字符串也能调”
| 方法 | 场景 | 示例 |
|---|---|---|
match | 一次性抓所有 | 'a1b2c3'.match(/\d/g) // ["1","2","3"] |
replace | 替换、模板 | 见下文“日志脱敏” |
split | 按正则劈开 | `'1,2 |
4. 回调式 replace——“替换也能玩花活”
// 把下划线命名改成小驼峰
const snake = 'user_name_and_age';
const camel = snake.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
console.log(camel); // "userNameAndAge"
修饰符不只是可选项——“小字母,大差别”
| 修饰符 | 含义 | 实战场景 |
|---|---|---|
g | 全局 | 找出所有匹配,而不是第一个就停 |
i | 忽略大小写 | 用户验证码输入不区分大小写 |
m | 多行 | ^ $ 可以匹配行首行尾,而不是整个字符串首尾 |
s | dotAll | 让 . 匹配换行符,采集 HTML 块时爽翻 |
u | Unicode | 匹配 emoji、生僻字必备 |
y | sticky | 粘连匹配,从 lastIndex 开始,做词法分析神器 |
组合技案例:
// 把 markdown 的每一级标题提取出来
const md = `# ES2025 新特性\n## 正则扩展\n### 命名分组`;
const heading = /^([#]+)\s+(.+)$/gim;
let h;
while ((h = heading.exec(md)) !== null) {
console.log(`${h[1].length} 级标题:${h[2]}`);
}
// 1 级标题:ES2025 新特性
// 2 级标题:正则扩展
// 3 级标题:命名分组
正则性能那些事儿——“贪婪与懒惰的拉锯战”
1. 贪婪 vs 懒惰
// 想匹配第一个 </div>
const html = '<div><div>hi</div></div>';
html.match(/<div>.*<\/div>/); // 贪婪:整串都吃掉
html.match(/<div>.*?<\/div>/); // 懒惰:只到第一个 </div>
懒惰用 *?、+?,但别高兴太早:懒惰只是“回溯得少”,不是“零开销”。
2. 回溯爆炸——“一个(.*?)当场去世”
来看核弹:
const re = /(a+)+b/;
re.test('aaaaaaaaaaaaaaaaac'); // 几十毫秒变几秒
原因:(a+)+ 让引擎陷入“我该在哪一层分组结束”的哲学思考,指数级回溯。
解决:
- 具体化字符集,别写太宽泛的
.或\s\S - 使用“独占模式”——JavaScript 目前不支持
(?>...),但可以用“反向否定预查”曲线救国:
const atomic = /(?=(a+))\1b/; // 先捕获,再引用,等价独占
3. 提前编译 & 缓存
// 全局缓存正则,避免在 React render 里疯狂 new
const REGEX_CACHE = new Map();
function getRegex(pattern, flags) {
const key = `${pattern}_${flags}`;
if (!REGEX_CACHE.has(key)) {
REGEX_CACHE.set(key, new RegExp(pattern, flags));
}
return REGEX_CACHE.get(key);
}
表单验证中的经典用法——“一行正则行天下?别冲动”
1. 手机号
const mobile = /^1[3-9]\d{9}$/;
说明:
- 第二位 3-9:随运营商新号段更新,把 199/198/195 都囊括
- 全 11 位:拒绝 012、106 开头
2. 邮箱——“能跑就行” vs “RFC5322 完全体”
// 80% 场景够用:
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// 100% 场景请放弃正则,用解析库或浏览器 `<input type="email>` 原生校验
原因:邮箱的注释、引号、IPv6 域名能把正则写到 6KB,别为难自己。
3. 身份证——“18 位还带校验码”
function validateId(id) {
const reg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$/i;
if (!reg.test(id)) return false;
// 校验码
const weights = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2];
const checksums = '10X98765432';
let sum = 0;
for (let i = 0; i < 17; i++) sum += id[i] * weights[i];
return checksums[sum % 11] === id[17].toUpperCase();
}
教训:别光写“看起来像”,校验码算法能挡住 90% 的假号。
4. 密码强度——“一行正则不现实”
// 至少 8 位,同时含大小写、数字、特殊字符
const strong = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/;
解释:
- 用“零宽正向预查”
(?=.*[a-z])分别检查四类字符 - 不含空格,避免用户复制粘贴带空白
- 但别迷信:长口令“correct horse battery staple”比 “P@ssw0rd” 更安全,考虑用“熵值估算”替代“复杂字符”。
文本处理实战技巧——“日志、敏感词、模板,一把梭”
1. 日志脱敏——“一行代码过等保”
// 把手机号、银行卡、身份证全打码
function desensitize(log) {
return log
.replace(/\b1[3-9]\d{5}(\d{4})\b/g, '****$1') // 手机
.replace(/\b(\d{4})\d{8}(\d{4})\b/g, '$1****$2') // 银行卡
.replace(/\b(\d{6})\d{8}(\w{4})\b/g, '$1****$2'); // 身份证
}
console.log(desensitize('login user=13800138000, id=110105199003078888'));
// "login user=****13800, id=110105****8888"
2. 敏感词过滤——“DFA 太慢?先上正则挡一波”
const keywords = ['996','icu','加班'];
const escape = w => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(keywords.map(escape).join('|'), 'gi');
function filter(text) {
return text.replace(regex, m => '*'.repeat(m.length));
}
console.log(filter('今天又要996,进了icu')); // "今天又要***,进了***"
注意:正则不擅长“跳脱特殊符号”+“多音字”+“嵌套关键词”,高压场景请上 DFA + 人工审核。
3. 模板引擎——“自己撸一个 mustache”
function tinyRender(tpl, data) {
return tpl.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] ?? '');
}
const html = tinyRender('Hello {{name}}, order No.{{id}}', {name:'李雷',id:'666'});
console.log(html); // "Hello 李雷, order No.666"
遇到“不匹配”怎么办——“调试正则的六脉神剑”
- console.log 不够用?先打印
.source:
console.log(`当前正则是:/${regex.source}/${regex.flags}`);
- 用
String.match看返回 null 还是空数组,分清“没匹配”与“匹配了空串” - 把
g标志先摘掉,避免lastIndex干扰 - 在 regexr.com(线上工具,非广告,纯友情)里开“解释模式”,一行行高亮
- 把目标字符串拆短,二分定位:一半能匹配,一半不能,快速找到“断点字符”
- 终极杀器:把正则拆成“注释模式”——
const re = new RegExp(`
^ # 行首
(?=.*[a-z]) # 至少一个小写
(?=.*[A-Z]) # 至少一个大写
.{8,} # 长度 8+
$ # 行尾
`.replace(/\s+#.*|\s+/g,'')); // 压缩后生产环境再用
写出可维护正则的秘诀——“命名、注释、模块化”
1. 命名分组 + 注释
const DATE_REGEX = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const {year, month, day} = '2025-01-07'.match(DATE_REGEX).groups;
2. 把正则当“配置”抽离
// regex-config.js
export const REGEX = {
mobile: /^1[3-9]\d{9}$/,
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
// 集中写注释,方便同步给后端
};
3. 单元测试覆盖
import {REGEX} from './regex-config';
test('mobile', () => {
expect(REGEX.mobile.test('13800138000')).toBe(true);
expect(REGEX.mobile.test('12800138000')).toBe(false); // 第二位 2 不合法
});
那些年踩过的正则大坑——“血与泪的 FAQ”
| 坑位 | 场景 | 现场翻车 | 正确姿势 |
|---|---|---|---|
| UTF-8 编码 | emoji 分割 | /./g 把“😂”切成两个乱码 | 用 u 修饰符:/./gu |
| 特殊字符 | 用户输入“C++” | 直接 new RegExp('C++') 报错 | 先转义:`user.replace(/[.*+?^${}() |
| 跨浏览器 | IE11 不支持 lookbehind | (?<=@)\w+ 直接 SyntaxError | 用捕获组+replace 模拟,或上 babel-plugin-transform-modern-regex |
| 贪婪回溯 | 超长空格 | /\s+\s+/g 匹配 10KB 空格卡死 | 量词细化,或换非正则(字符串 split) |
让正则更聪明的小技巧——“和现代 JS 打成一片”
1. 动态构造 + 数组方法
// 生成“匹配任意一项”的正则
const langs = ['js','ts','jsx','tsx'];
const reg = new RegExp(`\\.(${langs.join('|')})$`, 'i');
console.log(reg.test('index.tsx')); // true
2. 与 Intl.Collator 搭配做“模糊拼音”
// 把拼音相同字符归一类,再构造正则
const pinyinMap = {
'a': '[aāáǎà]', 'e': '[eēéěè]', 'i': '[iīíǐì]'
};
function buildPinyinRegex(keyword) {
const escaped = [...keyword].map(ch => pinyinMap[ch]||ch).join('');
return new RegExp(escaped, 'i');
}
3. 结合 Proxy 做“可链式”验证器
const Validator = new Proxy({}, {
get(_, prop) {
return (regex) => (value) => ({ pass: regex.test(value), prop, value });
}
});
const check = Validator.mobile(/^1[3-9]\d{9}$/);
console.log(check('13800138000')); // {pass: true, prop: 'mobile', value: '13800138000'}
结语——“正则不是银弹,却是瑞士军刀”
写正则就像做菜:盐放少了没味道,放多了齁嗓子;但掌握了火候,你就能把“字符串”这盘菜炒出花来。别指望一篇万字长文就让你变成“正则大师”,真正的高手都是在凌晨三点和 CPU 100% 的拉锯战里淬炼出来的。把这篇文章收藏起来,下次遇到“为啥不匹配”的时候,翻一翻、调一调、笑一笑,然后——
const life = /bug/g;
while (life.test('life')) {
console.log('fix one more');
}
祝你写最少的代码,匹配最多的需求,我们评论区或者 StackOverflow 见。
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!
| 专栏系列(点击解锁) | 学习路线(点击解锁) | 知识定位 |
|---|---|---|
| 《微信小程序相关博客》 | 持续更新中~ | 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等 |
| 《AIGC相关博客》 | 持续更新中~ | AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结 |
| 《HTML网站开发相关》 | 《前端基础入门三大核心之html相关博客》 | 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识 |
| 《前端基础入门三大核心之JS相关博客》 | 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。 通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心 | |
| 《前端基础入门三大核心之CSS相关博客》 | 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页 | |
| 《canvas绘图相关博客》 | Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化 | |
| 《Vue实战相关博客》 | 持续更新中~ | 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅 |
| 《python相关博客》 | 持续更新中~ | Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具 |
| 《sql数据库相关博客》 | 持续更新中~ | SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能 |
| 《算法系列相关博客》 | 持续更新中~ | 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维 |
| 《IT信息技术相关博客》 | 持续更新中~ | 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识 |
| 《信息化人员基础技能知识相关博客》 | 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方 | |
| 《信息化技能面试宝典相关博客》 | 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面 | |
| 《前端开发习惯与小技巧相关博客》 | 持续更新中~ | 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等 |
| 《photoshop相关博客》 | 持续更新中~ | 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结 |
| 日常开发&办公&生产【实用工具】分享相关博客》 | 持续更新中~ | 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具 |
吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

&spm=1001.2101.3001.5002&articleId=156664722&d=1&t=3&u=6e74fd82b384453fbbad949cfa314eac)
3540

被折叠的 条评论
为什么被折叠?



