主题
2-18正则表达式和元字符(二)
本页对应 第二章WebAPIs,共 4 段视频,约 29 分钟。
学习目标
- 能列举并解释字符类元字符
\d、\D、\w、\W、\s、\S的匹配含义,并能说明大小写形式互为相反匹配。 - 能使用字符类和量词读懂并改写日期格式正则,说明年、月、日各分组及分隔符的匹配规则。
- 能使用
replace()完成字符串替换,说明两个参数的作用、返回值为替换后的新字符串且不修改原字符串。 - 能使用修饰符
i和g控制正则匹配细节,并实现忽略大小写替换与全局替换。 - 能编写隐藏手机号中间四位的案例,使用分组捕获
$1、$2、repeat()和模板字符串实现手机号脱敏及仅显示后四位的变体。 - 能安装并使用 VS Code 插件 any-rule,通过“正则大全”按关键字检索并插入手机号、QQ 号等正则;能结合
change事件在表单值改变时触发正则校验或替换。
知识点
为什么需要 any-rule 这类正则插件

为什么需要: 正则学到后面会发现,很多表达式“看懂不难,默写很难”。转写稿里也明确说:正则“没学过是真看不懂,学过了也不一定能写得出来、背得过”,大多数场景是拿现成规则来改、来用。
怎么想: 不要把插件当成“代替学习”的工具,而是当成“查表工具”。字符类、替换、修饰符这些基础仍要懂,否则插件生成的正则复制过来也不会改。
怎么做: 在 VS Code 扩展商店搜索 any-rule,安装后右键选择“正则大全”,再按关键字搜索,例如“手机号”,会看到严谨、宽松等不同版本,点选后即可插入到代码里。
易错点: 插件生成的是“正则文本”,复制后要检查锚点、转义和标志位;表单验证常用 test,字符串替换常用 replace,两者不能混着用。
怎么想:先会改,再会写
为什么需要: 转写稿里反复强调,学正则不是为了手动背长表达式,而是为了验证表单、做替换等操作。
怎么想: 拿到插件生成的正则后,先看它匹配什么:是数字、字母数字下划线,还是手机号;再看有没有 ^、$、括号捕获组和 g、i 修饰符。
怎么做: 例如手机号隐藏案例中,先用正则把手机号划分成“前三位、中间四位、后四位”,再用 replace 替换中间四位。核心代码如下:
js
const phone = '13611112222'
const reg = /^(\d{3})\d{4}(\d{4})$/
const result = phone.replace(reg, '$1****$2')
console.log(result) // 136****2222这里 \d 是字符类,表示 0 到 9 的任意数字;(\d{3}) 和 (\d{4}) 是捕获组,替换字符串里的 $1、$2 分别取第一个、第二个小括号里的内容。
易错点: replace 默认只替换第一个匹配项,要全部替换需加 g;字符串本身不会被修改,replace 返回替换后的新字符串。转写稿中第一次只替换了一个“前端”,就是因为没有加 g。
怎么做:安装插件并插入正则
为什么需要: 手机号、QQ 号、网址、银行卡等规则写起来长,插件能减少查找和手写成本。
怎么想: 先明确业务要“严谨”还是“宽松”。转写稿演示了手机号有严谨、宽松、最宽松等选项,选错会导致该通过的校验不通过,或不该通过的却通过。
怎么做: 在 VS Code 中右键,选择“正则大全”,输入关键字,例如“手机号”,点击需要的条目,正则就会插入光标处。视频里还演示了 QQ 号、网址等条目。
易错点: 不要直接背插件里的长正则;插入后要按业务补 ^、$ 做整串校验,替换场景再考虑是否加 g。
用插件生成的正则做手机号隐藏调试
为什么需要: 只显示后四位也是常见需求,例如打车场景。
怎么想: 可以把后四位放进捕获组,前面七位用星星替换;也可以先写七个星星,再拼 $1。
怎么做:
js
const phone = '13611112222'
const result2 = phone.replace(/^\d{7}(\d{4})$/, '*******$1')
console.log(result2) // *******2222
const stars = '*'.repeat(7)
const result3 = phone.replace(/^\d{7}(\d{4})$/, `${stars}$1`)
console.log(result3) // *******2222repeat 是字符串方法,不是正则量词;模板字符串里调用方法要写成 \${stars}。
易错点: 捕获组编号要和括号顺序对应;$1 写在普通字符串和模板字符串里都表示替换引用,但模板字符串中的 \${...} 会先执行 JavaScript 表达式。调试时先 console.log 看替换结果,再写入页面,例如 document.querySelector('.phone').innerHTML = result。
先说明:本节转写稿与 change 事件不匹配
本节笔记只有一句核心内容:给 input 注册 change 事件,值被修改并且失去焦点后触发。但提供的转写稿讲的是正则字符类、replace 替换、修饰符、隐藏手机号中间四位以及 any-rule 插件,和“change 事件”不是同一主题。因此下面不把正则内容硬塞进 change 事件教程,只依据本节笔记扩写;转写稿中“原字符”应为“元字符”、“政则”应为“正则”等同音转写问题,也与本节无关,不展开。
change 事件到底解决什么问题
为什么需要:文本框如果注册 input 事件,用户每输入一个字符就会触发一次。做实时搜索、字数统计时这很有用;但做“输入完成后确认”“失焦后校验”“选择后联动”时,频繁触发反而不合适。change 的价值在于:它不追踪每一次按键,而是在值被修改并且失去焦点后给出一次确认。
怎么想:把 change 理解成“用户对这次输入做了一个阶段性提交”。对文本输入框来说,用户还在输入时不算提交;当用户点击页面空白、切换到下一个控件,也就是失去焦点后,浏览器才检查值是否变化,变化了才触发 change。
怎么做:先获取 input,再用 addEventListener('change', handler) 注册。事件处理函数里通过 e.target.value 或 input.value 读取当前值。
易错点:不要用 change 做逐字实时校验;不要以为只要失焦就触发,值没变不会触发;不要混淆 onchange 属性写法和 addEventListener 写法。
用代码观察 change 的触发时机
为什么需要:只看定义容易把 change、input、blur 混在一起。用控制台日志观察触发顺序,能直观看到“输入中”和“失焦后”的差别。
怎么想:同一个 input 同时注册 input、change、blur 三个事件。输入文字时观察 input 是否连续触发;点击空白失焦时观察 change 是否出现;不改值直接失焦时观察 change 是否出现。
怎么做:
js
<label>
用户名:
<input id="username" type="text" value="小明">
</label>
<p id="tip"></p>
<script>
const input = document.querySelector('#username');
const tip = document.querySelector('#tip');
input.addEventListener('input', function () {
console.log('input 触发,当前值:', input.value);
});
input.addEventListener('change', function (e) {
console.log('change 触发,当前值:', e.target.value);
tip.textContent = `已确认的值:${e.target.value}`;
});
input.addEventListener('blur', function () {
console.log('blur 触发');
});
</script>调试时打开控制台:在输入框里连续输入,会看到多次 input;点击页面空白后,如果值确实变了,会看到 change;如果什么都没改就失焦,通常不会看到 change。不要依赖 change 与 blur 的先后顺序去写复杂业务逻辑,业务判断应放在 change 处理函数内部完成。
易错点:用 JavaScript 直接写 input.value = '新值' 不会自动触发 change,它只改变值,不模拟用户操作;如果确实需要,要手动派发事件。另一个易错点是读取值时写错对象,应该用 e.target.value 或已获取的 input 元素。
不同表单控件的 change 表现
为什么需要:change 不只用于文本框。复选框、单选框、下拉框也会触发,但触发时机和文本框不完全一样。
怎么想:文本输入框需要“值改变 + 失去焦点”;复选框、单选框、下拉框更接近“用户完成一次选择”就触发。写代码前先确认控件类型,不要把所有控件都套成“失焦才触发”。
怎么做:给 select 注册 change,选择不同选项后读取 select.value;给 checkbox 注册 change,切换后读取 checkbox.checked。例如:
html
<select id="city">
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
<script>
const city = document.querySelector('#city');
city.addEventListener('change', function (e) {
console.log('选择的城市:', e.target.value);
});
</script>易错点:不要用文本框的“失焦”经验解释所有控件的 change;不要忘记 select 读取的是 value,checkbox 读取的是 checked;不要在事件外反复查询 DOM,能缓存元素就缓存。
什么时候该用 change,什么时候不该用
为什么需要:事件选错会导致体验变差。实时搜索用 change 会显得“慢半拍”;而最终确认、失焦校验、下拉联动用 input 又可能触发过频。
怎么想:需要“每次输入都响应”就用 input;需要“用户完成输入后再确认”就用 change;需要“只要失焦就做事”才用 blur。三者目的不同,不要互相替代。
怎么做:表单校验可以监听 change,在用户离开输入框后给出提示;实时联想、字数统计监听 input;纯 UI 失焦效果监听 blur。
易错点:把 change 当成“值一变就触发”是常见误解;值改回原值后是否触发,受浏览器对“值是否改变”的判断影响,实际开发中应以目标浏览器测试为准,不要写依赖这种边界行为的核心逻辑。
课件图示








代码示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>replace 替换与修饰符</title>
</head>
<body>
<script>
const str = '欢迎大家来学前端,谢谢大家一定能学好前端,都成为前端大神'
// 1. 没有修饰符:只替换第一个
const str1 = str.replace(/前端/, 'web')
console.log(str1) // 只有第一个"前端"变成了 web
// 2. g 修饰符(global 全局):全部替换
const str2 = str.replace(/前端/g, 'web')
console.log(str2) // 三个"前端"全部变成 web
// 3. 把关键字替换成星星,实现隐藏
console.log(str.replace(/前端/g, '**'))
// 4. i 修饰符(ignore 忽略):忽略大小写
console.log(/a/.test('a')) // true
console.log(/A/.test('a')) // false
console.log(/A/i.test('a')) // true
// 5. g 和 i 可以一起用
console.log('A a A'.replace(/a/gi, 'x'))
</script>
</body>
</html>要点解释与易错点
replace(规则, 替换成什么)有两个参数,第一个参数可以是正则表达式,第二个参数是替换后的内容。replace不会修改原字符串,而是返回一个替换完成后的新字符串,所以要拿变量接住返回值。- 不加
g只替换第一个匹配项,这是最容易出错的地方;需要全部替换必须在正则最后加g。 i是 ignore 的缩写,加在正则最后的斜杠后面,表示不区分大小写;g是 global,表示全局匹配。- 修饰符写在正则的最后一个斜杠之后:
/前端/g,不要写成/g前端/。
js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>隐藏手机号中间四位</title>
</head>
<body>
<div class="phone"></div>
<script>
const tel = '13611112222'
// 需求一:隐藏中间四位,保留前三位和后四位
// 正则把手机号划分成三段:(\d{3}) 前三位 \d{4} 中间四位 (\d{4}) 后四位
const reg = /^(\d{3})\d{4}(\d{4})$/
const str = tel.replace(reg, '$1****$2')
console.log(str) // 136****2222
document.querySelector('.phone').innerHTML = `恭喜 ${str} 的小伙伴得到了我们的奖品`
// 需求二:只显示后四位,前七位用星星隐藏
const reg2 = /^\d{7}(\d{4})$/
console.log(tel.replace(reg2, '*******$1'))
// 需求二升级:星星太多不想一个个写,用 repeat 重复字符串
console.log('*'.repeat(7)) // *******
console.log('开心'.repeat(8)) // 开心开心...
console.log(tel.replace(reg2, `${'*'.repeat(7)}$1`))
</script>
</body>
</html>要点解释与易错点
\d是字符类,等价于[0-9],表示 0-9 之间的任意一个数字;\D则相反,表示"不是数字"。大小写含义正好相反,不要写混。{3}、{7}是量词,表示前面那个字符重复的次数;{1,2}表示重复 1 到 2 次。- 正则两端一定要加
^和$,否则只匹配到一部分也会替换,结果就会乱。 $1拿到的是第一个小括号里的内容,$2拿到第二个小括号里的内容,顺序按左括号出现的先后算。没加小括号的部分(中间四位)不会被$n取到。repeat()是字符串的方法,不是正则的,'*'.repeat(7)返回 7 个星号;正则里重复用的是量词。- 模板字符串里调用方法要写成
\${'*'.repeat(7)},不能直接写在单引号字符串里。 - 别忘
replace有返回值,要接住或者直接写进innerHTML。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>change 事件 + 正则校验</title>
<style>
input { padding: 6px; outline: none; }
.ok { border: 2px solid green; }
.err { border: 2px solid red; }
</style>
</head>
<body>
<input type="text" id="phone" placeholder="请输入手机号">
<script>
const input = document.querySelector('#phone')
// change:值被修改并且失去焦点后触发
input.addEventListener('change', function () {
// 手机号:11 位纯数字
const reg = /^\d{11}$/
this.className = reg.test(this.value) ? 'ok' : 'err'
})
</script>
</body>
</html>要点解释与易错点
change事件的触发条件是"值被修改并且失去焦点",只输入不离开输入框是不会触发的,这一点和input事件不同。- 校验用
reg.test(值),返回布尔值,比exec更适合做表单判断。 - 正则里
^表示开头、$表示结尾,漏掉就会出现"包含 11 位数字就算通过"的错误结果。 - 复杂正则不必手写,可以装 VS Code 的 any-rule 插件,右键选"正则大全",搜"手机号"等关键字直接取用;重点是学会拿正则去做验证。
常见错误
1. 替换只生效一次,后面的同名内容原样保留
- 现象:
str.replace(/前端/, 'web')执行后,只有第一处“前端”被替换。 - 原因:正则默认非全局匹配,
replace只处理第一个命中项。 - 修法:给正则加
g修饰符 ——str.replace(/前端/g, 'web');若要同时忽略大小写,写成/前端/gi。
2. 以为 replace 会改动原字符串,结果打印出来没变化
- 现象:
str.replace(...)之后console.log(str)仍是旧内容。 - 原因:字符串不可变,
replace是返回一个新字符串,不修改调用者。 - 修法:接住返回值 ——
const newStr = str.replace(...),或str = str.replace(...)。
3. 用 $1 拼接却拿到字面量 $1
- 现象:替换结果里直接出现
$1文本,而不是分组内容。 - 原因:正则里对应的部分没有用小括号分组,或者
$1写在了正则里而非替换字符串里;分组序号也数错了($1是第一个左括号,按左括号出现顺序编号)。 - 修法:先用小括号把要保留的片段括起来,再在第二个参数里按顺序引用
$1、$2;可用str.match(reg)先确认分组结果。
4. 模板字符串里写 repeat 没加 ${},输出一坨字面量
- 现象:页面上显示
'*'.repeat(7)$1这样的原文。 - 原因:反引号里不加
${}的部分都是纯文本,方法调用不会执行。 - 修法:写成
`\${'*'.repeat(7)}$1`,注意repeat是字符串的方法,repeat(7)表示重复 7 次。
5. 手机号正则不加首尾锚点,替换位置错乱
- 现象:号码位数不对时(如 10 位、12 位)依然被“成功”隐藏,或前几位被吃掉。
- 原因:没写
^和$,正则从任意位置开始匹配;分组数量与实际位数不匹配。 - 修法:写成
/^(\d{3})\d{4}(\d{4})$/,用^...$锁定整串,并保证3 + 4 + 4 = 11位。
6. 用 change 做实时校验,输入过程中毫无反应
- 现象:边打字边看不到提示,必须点别处才触发。
- 原因:
change的触发条件是“值被修改且失去焦点”,输入过程中不会触发。 - 修法:需要实时反馈改用
input事件;确实要等用户填完再校验(如手机号、邮箱)就用change或blur。
练习
1. 全局替换 + 忽略大小写 把 "JavaScript 很好玩,我喜欢 javascript,JAVASCRIPT 最强" 中所有大小写形式的 javascript 替换为 JS,并打印结果与原字符串对比,说明哪个变了。
2. 字符类判断 用 \w、\d、\s 写三个正则,分别判断以下字符串是否“只由字母数字下划线组成”“只由数字组成”“包含空白字符”: "abc_123"、"abc-123"、"123456"、"hello world"。
3. 手机号脱敏(两个需求) 给定 const tel = '13812345678':
- 需求 A:隐藏中间四位 →
138****5678,用分组 +$1 $2实现; - 需求 B:只显示后四位 →
*******5678,分别用「手写 7 个星号」和「repeat+ 模板字符串」两种写法实现。
4. 表单联动 页面上放一个输入框和一个展示区:用户输入手机号后失去焦点(change),若格式合法(用 ^\d{11}$ 或 any-rule 插件生成的正则校验),就在展示区输出脱敏后的号码;不合法则提示“请输入 11 位手机号”。
5. 插件实战 安装 any-rule 插件,右键 → 正则大全,分别取一个「手机号(宽松)」和「QQ 号」的正则,套进 test() 里验证 3 组正确数据和 3 组错误数据,记录哪条规则最符合你的业务需要。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 元字符之字符类 | 05:27 |
| 2 | 替换和修饰符 | 07:39 |
| 3 | 隐藏手机号中间四位的案例 | 13:22 |
| 4 | 推荐一个正则插件any-rule | 03:26 |
小结
- 字符类是“常见模式的简写”:
\d/\D、\w/\W、\s/\S,小写是“是”,大写是“不是”,每组都只匹配一个字符。 - 字符类让日期、手机号这类正则从“看不懂”变成“能读懂”,
2099-1-1这种写法靠\d{4}、\d{1,2}就能解释清楚。 replace(规则, 替换内容)两个参数缺一不可,规则可以是字符串也可以是正则;它返回新字符串,不改原串。- 修饰符写在正则最后一条斜杠之后:
i忽略大小写,g全局匹配——replace只替换一处,九成是因为漏了g。 - 小括号是“分组捕获”,
$1、$2按左括号顺序取值;脱敏案例就是靠“保留分组 + 中间塞星号”完成的。 repeat(n)是字符串方法,用来重复字符串;在模板字符串中必须用${}包裹才会执行。change是“改完并失焦”才触发,实时校验请换input;两者选哪个取决于业务要不要即时反馈。- 正则不必背,会改、会验证表单就够用;any-rule 这类插件能直接生成常用正则,把精力放在“用对地方”上。