【Web APIs-Day6&7】正则表达式与综合实战
📺 对应视频:P138-P151 | 🎯 核心目标:掌握正则语法、表单验证、综合项目(登录页/首页/放大镜)
一、正则表达式
1.1 什么是正则表达式?
正则表达式(Regular Expression)是用来匹配字符串的模式,常用于表单验证、字符串搜索替换。
// 创建正则
const reg1 = /hello/ // 字面量(推荐)
const reg2 = new RegExp('hello') // 构造函数(动态模式时用)
const reg3 = /hello/i // i:忽略大小写
const reg4 = /hello/g // g:全局匹配(查找所有)
const reg5 = /hello/m // m:多行匹配
const reg6 = /hello/gi // 多个修饰符组合
1.2 test() 和 exec()
const reg = /\d+/
// test():测试是否匹配,返回布尔值(最常用)
reg.test('abc123') // true
reg.test('abc') // false
// exec():返回匹配结果数组,无匹配返回 null
reg.exec('abc123def')
// ['123', index: 3, input: 'abc123def', groups: undefined]
1.3 字符串的正则方法
const str = 'Hello World hello'
// match():返回匹配的内容
str.match(/hello/i) // ['Hello', index: 0, ...](只找第一个)
str.match(/hello/ig) // ['Hello', 'hello'](加 g 找所有)
// matchAll():返回所有匹配的迭代器(ES2020)
const matches = [...str.matchAll(/hello/ig)]
matches.forEach(m => console.log(m[0], m.index))
// replace():替换
str.replace(/hello/i, 'Hi') // 'Hi World hello'(只替换第一个)
str.replace(/hello/ig, 'Hi') // 'Hi World Hi'(替换所有)
'2024-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1') // '01/15/2024'
// replaceAll()(ES2021)
str.replaceAll('hello', 'Hi') // 等价于 /hello/ig
// split():用正则分割
'a1b2c3'.split(/\d/) // ['a', 'b', 'c', '']
// search():返回第一个匹配的索引
str.search(/world/i) // 6(找到),-1(未找到)
二、正则语法详解
2.1 元字符
. 任意字符(除换行符)
\d 数字 [0-9]
\D 非数字
\w 单词字符 [a-zA-Z0-9_]
\W 非单词字符
\s 空白字符(空格、Tab、换行)
\S 非空白字符
\b 单词边界
\B 非单词边界
^ 行首(在字符类中表示非)
$ 行尾
2.2 字符类
[abc] a、b 或 c
[^abc] 不是 a、b、c
[a-z] a 到 z(小写字母)
[A-Z] A 到 Z(大写字母)
[0-9] 数字
[a-zA-Z0-9] 字母或数字
2.3 量词
* 0次或多次({0,})
+ 1次或多次({1,})
? 0次或1次({0,1})
{n} 恰好 n 次
{n,} 至少 n 次
{n,m} n 到 m 次
// 默认贪婪匹配(尽量多匹配)
'aabab'.match(/a.+b/) // ['aabab'](贪婪)
// 加 ? 变懒惰匹配(尽量少匹配)
'aabab'.match(/a.+?b/) // ['aab'](懒惰)
2.4 分组与捕获
// 分组 ()
/(\d{4})-(\d{2})-(\d{2})/.exec('2024-01-15')
// ['2024-01-15', '2024', '01', '15', index: 0, ...]
// 索引1开始是捕获组的内容
// 命名捕获组(ES2018)
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/.exec('2024-01-15').groups
// { year: '2024', month: '01', day: '15' }
// 非捕获组 (?:...)
/(?:foo)+/.test('foofoo') // 只匹配不捕获
// 或 |
/cat|dog/.test('I have a cat') // true
/(red|blue|green) apple/.test('red apple') // true
2.5 断言(零宽断言)
// 正向前瞻:x(?=y) → x 后面跟着 y
'100px'.match(/\d+(?=px)/) // ['100'](只匹配数字,px不包含在结果中)
// 负向前瞻:x(?!y) → x 后面不跟 y
'100em 200px'.match(/\d+(?!px)/g) // ['100']
// 正向后顾:(?<=y)x → x 前面是 y
'$100'.match(/(?<=$)\d+/) // ['100']
// 负向后顾:(?<!y)x
三、常用正则模式(速查)
// 手机号(11位,1开头)
/^1[3-9]\d{9}$/
// 邮箱
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/
// 身份证(18位)
/^\d{17}[\dXx]$/
// 密码(6-16位,字母+数字)
/^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{6,16}$/
// 中文字符
/[\u4e00-\u9fa5]/
// URL
/^https?://[\w.-]+(:\d+)?(/[\w-._~:/?#[]@!$&'()*+,;=]*)?$/
// 去除首尾空格
/^\s+|\s+$/g
str.replace(/^\s+|\s+$/g, '') // 等价于 str.trim()
// 去除所有空格
str.replace(/\s/g, '')
// 连续空格替换为单个空格
str.replace(/\s+/g, ' ')
四、表单验证实战
4.1 实时验证框架
// 通用验证函数
function validate(input, rules) {
const value = input.value.trim()
const errorEl = input.nextElementSibling // 错误提示元素
for (const rule of rules) {
if (!rule.test(value)) {
errorEl.textContent = rule.message
errorEl.style.display = 'block'
input.classList.add('error')
return false
}
}
errorEl.style.display = 'none'
input.classList.remove('error')
return true
}
// 使用示例
const usernameInput = document.querySelector('#username')
usernameInput.addEventListener('blur', () => {
validate(usernameInput, [
{ test: v => v.length >= 4, message: '用户名至少4个字符' },
{ test: v => /^[a-zA-Z\d_]+$/.test(v), message: '只能包含字母、数字、下划线' }
])
})
4.2 完整登录页验证
const form = document.querySelector('#login-form')
const usernameEl = document.querySelector('#username')
const passwordEl = document.querySelector('#password')
const validators = {
username: [
{ test: v => v !== '', message: '请输入用户名' },
{ test: v => v.length >= 4, message: '用户名至少4个字符' }
],
password: [
{ test: v => v !== '', message: '请输入密码' },
{ test: v => v.length >= 6, message: '密码至少6个字符' },
{ test: v => /^(?=.*[a-zA-Z])(?=.*\d)/.test(v), message: '密码需包含字母和数字' }
]
}
// 失焦验证
usernameEl.addEventListener('blur', () => validate(usernameEl, validators.username))
passwordEl.addEventListener('blur', () => validate(passwordEl, validators.password))
// 提交验证
form.addEventListener('submit', e => {
e.preventDefault()
const isValidUser = validate(usernameEl, validators.username)
const isValidPass = validate(passwordEl, validators.password)
if (isValidUser && isValidPass) {
// 提交登录请求
console.log('提交登录:', {
username: usernameEl.value,
password: passwordEl.value
})
}
})
五、图片放大镜效果
5.1 原理
1. 小图区域:鼠标移入显示遮罩层
2. 遮罩层跟随鼠标移动(限制在小图范围内)
3. 大图根据遮罩层位置等比例移动(放大显示对应区域)
遮罩移动比例 = 大图移动距离 / (大图尺寸 - 大图展示区尺寸)
5.2 核心代码
const smallBox = document.querySelector('.small-box')
const mask = document.querySelector('.mask')
const bigBox = document.querySelector('.big-box')
const bigImg = bigBox.querySelector('img')
smallBox.addEventListener('mouseenter', () => {
mask.style.display = 'block'
bigBox.style.display = 'block'
})
smallBox.addEventListener('mouseleave', () => {
mask.style.display = 'none'
bigBox.style.display = 'none'
})
smallBox.addEventListener('mousemove', e => {
const { left, top } = smallBox.getBoundingClientRect()
// 鼠标在小图内的位置
let x = e.clientX - left - mask.offsetWidth / 2
let y = e.clientY - top - mask.offsetHeight / 2
// 限制遮罩在小图范围内
x = Math.max(0, Math.min(x, smallBox.offsetWidth - mask.offsetWidth))
y = Math.max(0, Math.min(y, smallBox.offsetHeight - mask.offsetHeight))
mask.style.left = x + 'px'
mask.style.top = y + 'px'
// 大图移动(等比例,方向相反)
const ratioX = x / (smallBox.offsetWidth - mask.offsetWidth)
const ratioY = y / (smallBox.offsetHeight - mask.offsetHeight)
bigImg.style.left = -ratioX * (bigImg.offsetWidth - bigBox.offsetWidth) + 'px'
bigImg.style.top = -ratioY * (bigImg.offsetHeight - bigBox.offsetHeight) + 'px'
})
六、知识图谱
正则表达式与综合实战
├── 正则基础
│ ├── 创建:/pattern/flags 或 new RegExp()
│ ├── 测试:test()(布尔)/ exec()(结果数组)
│ └── 字符串方法:match/replace/split/search
├── 正则语法
│ ├── 元字符:. \d \w \s ^ $
│ ├── 字符类:[abc] [a-z] [^abc]
│ ├── 量词:* + ? {n} {n,m}(贪婪/懒惰)
│ └── 分组:() 捕获 (?:) 非捕获 (?<name>) 命名
├── 常用模式:手机号、邮箱、密码强度
├── 表单验证
│ ├── 失焦(blur)验证 + 实时(input)反馈
│ └── 提交前全量验证
└── 综合实战
└── 放大镜:getBoundingClientRect + 比例计算
七、高频面试题
Q1:正则的贪婪和懒惰模式是什么?
贪婪(默认):尽量多匹配。
/a.+b/匹配'aabab'得到'aabab'(最长匹配)。 懒惰(加?):尽量少匹配。/a.+?b/匹配'aabab'得到'aab'(最短匹配)。
Q2:如何替换字符串中所有的某个字符?
// 方式一:正则加 g 标志
'hello world'.replace(/o/g, '0') // 'hell0 w0rld'
// 方式二:replaceAll(ES2021)
'hello world'.replaceAll('o', '0')
Q3:表单验证一般在什么时机触发?
①
blur(失焦):用户完成输入后立即反馈;②input(实时):实时显示字符计数等;③submit(提交):最终全量校验,防止绕过前两步。三者配合使用体验最好。
⬅️ 上一篇:Web APIs Day5 - BOM与本地存储 ➡️ 下一篇:JS进阶Day1 - 作用域、闭包与ES6