最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
前端正则表达式表单验证与字符串处理高频场景实战合辑
时间:2026-06-07 08:25:47 编辑:袖梨 来源:一聚教程网
1. 正则表达式基础回顾
1.1 基本概念
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,我们可以使用两种方式创建正则表达式:

// 字面量方式const regex1 = /abc/;// 构造函数方式const regex2 = new RegExp('abc');1.2 常用元字符
| 元字符 | 描述 | 示例 |
|---|---|---|
. | 匹配除换行符外的任意字符 | /a.c/ 匹配 “abc”, “aac” |
^ | 匹配字符串的开始 | /^abc/ 匹配以abc开头的字符串 |
$ | 匹配字符串的结束 | /abc$/ 匹配以abc结尾的字符串 |
* | 匹配前面的子表达式零次或多次 | /ab*c/ 匹配 “ac”, “abc”, “abbc” |
+ | 匹配前面的子表达式一次或多次 | /ab+c/ 匹配 “abc”, “abbc” |
? | 匹配前面的子表达式零次或一次 | /ab?c/ 匹配 “ac”, “abc” |
[] | 字符集合,匹配其中任意一个字符 | /[abc]/ 匹配 “a”, “b”, “c” |
d | 匹配数字,等价于[0-9] | /d+/ 匹配一个或多个数字 |
w | 匹配字母、数字、下划线 | /w+/ 匹配单词字符 |
s | 匹配空白字符 | /s+/ 匹配一个或多个空白 |
2. 表单验证场景实战
2.1 邮箱验证
邮箱验证是前端开发中最常见的需求之一。一个完善的邮箱正则应该考虑各种格式:
/** * 邮箱验证 - 基础版本 * 适合大多数场景 */function validateEmailBasic(email) { const regex = /^[w.-]+@[w.-]+.w+$/; return regex.test(email);}/** * 邮箱验证 - 严格版本 * 符合RFC 5322标准的大部分规则 */function validateEmailStrict(email) { const regex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; return regex.test(email);}/** * 邮箱验证 - 实用版本 * 平衡准确性和性能 */function validateEmailPractical(email) { const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/; return regex.test(email);}// 测试用例console.log(validateEmailBasic('[email protected]')); // trueconsole.log(validateEmailBasic('[email protected]')); // trueconsole.log(validateEmailBasic('[email protected]')); // trueconsole.log(validateEmailBasic('invalid.email')); // false2.2 手机号验证
不同国家的手机号格式不同,这里以中国手机号为例:
/** * 中国手机号验证 * 支持最新的号段 */function validateChinesePhone(phone) { const regex = /^1[3-9]d{9}$/; return regex.test(phone);}/** * 带区号的手机号验证 */function validatePhoneWithAreaCode(phone) { const regex = /^(?:+86)?1[3-9]d{9}$/; return regex.test(phone);}/** * 固话号码验证 */function validateLandline(phone) { const regex = /^(?:0[1-9]d{1,2}-)?[2-8]d{6,7}$/; return regex.test(phone);}// 测试用例console.log(validateChinesePhone('13800138000')); // trueconsole.log(validateChinesePhone('+8613800138000')); // falseconsole.log(validatePhoneWithAreaCode('+8613800138000')); // trueconsole.log(validateLandline('010-12345678')); // true2.3 身份证号验证
中国大陆的身份证号有严格的校验规则:
/** * 身份证号验证 * 包含15位和18位身份证号的验证 */function validateIDCard(idCard) { // 基本格式验证 const regex15 = /^[1-9]d{7}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]d|3[01])d{3}$/; const regex18 = /^[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]$/; if (idCard.length === 15) { return regex15.test(idCard); } else if (idCard.length === 18) { if (!regex18.test(idCard)) return false; // 校验码验证 const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']; let sum = 0; for (let i = 0; i < 17; i++) { sum += parseInt(idCard[i]) * weights[i]; } const checkCode = checkCodes[sum % 11]; return idCard[17].toUpperCase() === checkCode; } return false;}// 测试用例console.log(validateIDCard('11010519900307283X')); // trueconsole.log(validateIDCard('110105900307283')); // trueconsole.log(validateIDCard('123456789012345')); // false2.4 密码强度验证
密码强度验证通常需要满足多个条件:
/** * 密码强度验证 - 基础版本 */function validatePasswordBasic(password) { // 至少8位,包含字母和数字 const regex = /^(?=.*[A-Za-z])(?=.*d)[A-Za-zd@$!%*#?&]{8,}$/; return regex.test(password);}/** * 密码强度验证 - 中级版本 * 必须包含大小写字母、数字、特殊字符中的至少3种 */function validatePasswordMedium(password) { const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*?&])[A-Za-zd@$!%*?&]{8,}$/; return regex.test(password);}/** * 密码强度验证 - 高级版本 * 自定义规则,可配置不同强度等级 */function validatePasswordAdvanced(password, options = {}) { const { minLength = 8, maxLength = 20, requireUppercase = true, requireLowercase = true, requireNumbers = true, requireSpecialChars = false, specialChars = '@$!%*?&' } = options; if (password.length < minLength || password.length > maxLength) { return false; } let pattern = '^'; if (requireLowercase) pattern += '(?=.*[a-z])'; if (requireUppercase) pattern += '(?=.*[A-Z])'; if (requireNumbers) pattern += '(?=.*d)'; if (requireSpecialChars) pattern += `(?=.*[${specialChars.replace(/[.*+?^${}()|[]]/g, '$&')}])`; pattern += `[A-Za-zd${specialChars.replace(/[.*+?^${}()|[]]/g, '$&')}]{${minLength},${maxLength}}$`; const regex = new RegExp(pattern); return regex.test(password);}// 测试用例console.log(validatePasswordBasic('password123')); // trueconsole.log(validatePasswordMedium('Password123!')); // trueconsole.log(validatePasswordAdvanced('mypassword', {requireUppercase: false})); // true2.5 URL验证
URL格式验证需要考虑多种协议和格式:
/** * URL验证 - 基础版本 */function validateURLBasic(url) { const regex = /^https?://(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/; return regex.test(url);}/** * URL验证 - 完整版本 * 支持更多协议和格式 */function validateURLComplete(url) { const regex = /^(https?|ftp)://((([w-]+.)+[w-]+)|localhost)(:[0-9]+)?(/[w- ./?%&=]*)?$/; return regex.test(url);}/** * 提取URL中的域名 */function extractDomain(url) { const regex = /^(?:https?://)?(?:www.)?([^/]+)/; const match = url.match(regex); return match ? match[1] : null;}// 测试用例console.log(validateURLBasic('https://www.example.com')); // trueconsole.log(validateURLBasic('http://example.com/path/to/page')); // trueconsole.log(extractDomain('https://www.example.com/path')); // www.example.com2.6 IP地址验证
IP地址验证包括IPv4和IPv6:
/** * IPv4地址验证 */function validateIPv4(ip) { const regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; return regex.test(ip);}/** * IPv6地址验证 */function validateIPv6(ip) { const regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$/; return regex.test(ip);}/** * 通用IP地址验证 */function validateIP(ip) { return validateIPv4(ip) || validateIPv6(ip);}// 测试用例console.log(validateIPv4('192.168.1.1')); // trueconsole.log(validateIPv4('256.1.1.1')); // falseconsole.log(validateIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')); // true3. 字符串处理高频场景
3.1 HTML标签清理
在处理富文本内容时,经常需要清理HTML标签:
/** * 移除所有HTML标签 */function removeAllHTMLTags(html) { return html.replace(/<[^>]*>/g, '');}/** * 移除指定HTML标签 */function removeSpecificHTMLTags(html, tags) { const tagPattern = tags.map(tag => `<${tag}[^>]*>|</${tag}>`).join('|'); const regex = new RegExp(tagPattern, 'gi'); return html.replace(regex, '');}/** * 保留指定HTML标签,移除其他标签 */function keepSpecificHTMLTags(html, allowedTags) { const allowedPattern = allowedTags.map(tag => `<${tag}[^>]*>|</${tag}>`).join('|'); const allTagsPattern = /<[^>]*>/g; return html.replace(allTagsPattern, (match) => { const regex = new RegExp(allowedPattern, 'i'); return regex.test(match) ? match : ''; });}/** * 转义HTML特殊字符 */function escapeHTML(html) { const escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return html.replace(/[&<>"']/g, (match) => escapeMap[match]);}// 测试用例const htmlContent = '<div><p>Hello <script>alert("xss")</script>World!</p></div>';console.log(removeAllHTMLTags(htmlContent)); // "Hello World!"console.log(removeSpecificHTMLTags(htmlContent, ['script'])); // "<div><p>Hello World!</p></div>"console.log(keepSpecificHTMLTags(htmlContent, ['p'])); // "<p>Hello World!</p>"3.2 特殊字符过滤
处理用户输入时,经常需要过滤特殊字符:
/** * 移除非字母数字字符 */function removeNonAlphanumeric(str) { return str.replace(/[^a-zA-Z0-9]/g, '');}/** * 移除非中文字符 */function removeNonChinese(str) { return str.replace(/[^u4e00-u9fa5]/g, '');}/** * 移除非ASCII字符 */function removeNonASCII(str) { return str.replace(/[^x00-x7F]/g, '');}/** * 自定义字符过滤 */function filterCustomCharacters(str, allowedChars) { const pattern = `[^${allowedChars.replace(/[.*+?^${}()|[]]/g, '$&')}]`; const regex = new RegExp(pattern, 'g'); return str.replace(regex, '');}/** * 移除非打印字符 */function removeNonPrintable(str) { return str.replace(/[x00-x1Fx7F-x9F]/g, '');}// 测试用例console.log(removeNonAlphanumeric('Hello, 世界! 123')); // "Hello123"console.log(removeNonChinese('Hello, 世界! 123')); // "世界"console.log(filterCustomCharacters('Hello123!@#', 'a-zA-Z')); // "Hello"3.3 数字格式化
数字格式化在金融、电商等场景中非常常见:
/** * 千位分隔符格式化 */function formatNumberWithCommas(number) { return number.toString().replace(/B(?=(d{3})+(?!d))/g, ',');}/** * 货币格式化 */function formatCurrency(amount, currency = 'CNY') { const formatted = formatNumberWithCommas(amount); const symbols = { 'CNY': '¥', 'USD': '$', 'EUR': '€', 'GBP': '£' }; return `${symbols[currency] || ''}${formatted}`;}/** * 提取字符串中的数字 */function extractNumbers(str) { const matches = str.match(/d+(?:.d+)?/g); return matches ? matches.map(Number) : [];}/** * 科学计数法转普通数字 */function scientificToDecimal(numStr) { const match = numStr.match(/^(d+(?:.d+)?)[eE]([+-]?d+)$/); if (!match) return numStr; const [, digits, exponent] = match; const exp = parseInt(exponent, 10); const [integer, decimal = ''] = digits.split('.'); if (exp >= 0) { return integer + decimal.padEnd(exp + decimal.length, '0'); } else { const totalLength = integer.length + Math.abs(exp); const result = (integer + decimal).padStart(totalLength, '0'); return '0.' + result.slice(0, totalLength); }}// 测试用例console.log(formatNumberWithCommas(1234567)); // "1,234,567"console.log(formatCurrency(1234567, 'USD')); // "$1,234,567"console.log(extractNumbers('价格是123.45元,优惠了20%')); // [123.45, 20]console.log(scientificToDecimal('1.23e-3')); // "0.00123"3.4 字符串提取
从复杂文本中提取有用信息是正则表达式的强项:
/** * 提取URL参数 */function extractURLParams(url) { const params = {}; const match = url.match(/?([^#]+)/); if (match) { const searchParams = new URLSearchParams(match[1]); for (const [key, value] of searchParams) { params[key] = value; } } return params;}/** * 提取CSS属性 */function extractCSSProperties(cssText) { const properties = {}; const regex = /([a-z-]+)s*:s*([^;]+);?/gi; let match; while ((match = regex.exec(cssText)) !== null) { properties[match[1].trim()] = match[2].trim(); } return properties;}/** * 提取JSON字符串 */function extractJSON(str) { const jsonRegex = /{[^{}]*}/g; const matches = str.match(jsonRegex); if (!matches) return []; return matches.filter(match => { try { JSON.parse(match); return true; } catch { return false; } }).map(json => JSON.parse(json));}/** * 提取Markdown链接 */function extractMarkdownLinks(markdown) { const regex = /[([^]]+)](([^)]+))/g; const links = []; let match; while ((match = regex.exec(markdown)) !== null) { links.push({ text: match[1], url: match[2] }); } return links;}// 测试用例const url = 'https://example.com?name=John&age=30#section';console.log(extractURLParams(url)); // { name: 'John', age: '30' }const css = 'color: red; font-size: 16px; margin: 10px 20px;';console.log(extractCSSProperties(css)); // { color: 'red', 'font-size': '16px', margin: '10px 20px' }const markdown = 'Check out [Google](https://google.com) and [GitHub](https://github.com)';console.log(extractMarkdownLinks(markdown)); // [{ text: 'Google', url: 'https://google.com' }, ...]3.5 文本替换
文本替换是正则表达式的核心功能之一:
/** * 智能大小写转换 */function smartCase(str, type = 'camel') { switch (type) { case 'camel': return str.replace(/(?:^w|[A-Z]|bw)/g, (word, index) => { return index === 0 ? word.toLowerCase() : word.toUpperCase(); }).replace(/s+/g, ''); case 'pascal': return str.replace(/(?:^w|[A-Z]|bw)/g, word => word.toUpperCase()).replace(/s+/g, ''); case 'kebab': return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase().replace(/s+/g, '-'); case 'snake': return str.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase().replace(/s+/g, '_'); default: return str; }}/** * 电话号码格式化 */function formatPhoneNumber(phone, format = 'national') { const cleaned = phone.replace(/D/g, ''); switch (format) { case 'national': return cleaned.replace(/(d{3})(d{4})(d{4})/, '$1 $2 $3'); case 'international': return cleaned.replace(/(d{3})(d{4})(d{4})/, '+86 $1 $2 $3'); case 'dash': return cleaned.replace(/(d{3})(d{4})(d{4})/, '$1-$2-$3'); default: return cleaned; }}/** * 敏感词过滤 */function filterSensitiveWords(text, sensitiveWords, replacement = '***') { if (!Array.isArray(sensitiveWords) || sensitiveWords.length === 0) { return text; } const pattern = sensitiveWords.map(word => { return word.replace(/[.*+?^${}()|[]]/g, '$&'); }).join('|'); const regex = new RegExp(pattern, 'gi'); return text.replace(regex, replacement);}/** * 日期格式化 */function formatDateString(dateStr, format = 'YYYY-MM-DD') { const date = new Date(dateStr); if (isNaN(date.getTime())) return dateStr; const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return format .replace('YYYY', year) .replace('MM', month) .replace('DD', day) .replace('HH', hours) .replace('mm', minutes) .replace('ss', seconds);}// 测试用例console.log(smartCase('hello world', 'camel')); // "helloWorld"console.log(smartCase('hello world', 'pascal')); // "HelloWorld"console.log(formatPhoneNumber('13800138000', 'dash')); // "138-0013-8000"console.log(filterSensitiveWords('这是一段包含敏感词的文本', ['敏感词'], '[已过滤]')); // "这是一段包含[已过滤]的文本"4. 性能优化技巧
正则表达式的性能对大型应用至关重要:
4.1 避免回溯灾难
/** * 性能对比:贪婪 vs 懒惰匹配 */function comparePerformance() { const testString = 'a'.repeat(10000) + 'b'; // 贪婪匹配 - 可能导致性能问题 const greedyRegex = /a.*b/; // 懒惰匹配 - 更好的性能 const lazyRegex = /a.*?b/; // 独占匹配 - 最佳性能 const possessiveRegex = /a[^b]*b/; console.time('贪婪匹配'); greedyRegex.test(testString); console.timeEnd('贪婪匹配'); console.time('懒惰匹配'); lazyRegex.test(testString); console.timeEnd('懒惰匹配'); console.time('独占匹配'); possessiveRegex.test(testString); console.timeEnd('独占匹配');}4.2 预编译正则表达式
/** * 预编译正则表达式缓存 */class RegexCache { constructor() { this.cache = new Map(); } get(pattern, flags = '') { const key = `${pattern}_${flags}`; if (!this.cache.has(key)) { this.cache.set(key, new RegExp(pattern, flags)); } return this.cache.get(key); } clear() { this.cache.clear(); } size() { return this.cache.size; }}// 使用示例const regexCache = new RegexCache();const emailRegex = regexCache.get('^[w.-]+@[w.-]+.w+$');console.log(emailRegex.test('[email protected]')); // true4.3 使用合适的量词
/** * 选择合适的量词 */const optimizations = { // 使用具体的量词而不是通用量词 specific: { phone: /^d{11}$/, // 而不是 /^d+$/ postalCode: /^d{6}$/, // 而不是 /^d+$/ }, // 使用原子组减少回溯 atomic: { // 使用 (?>...) 原子组 pattern: /(?>a+)b/, }, // 使用 possessive 量词(在某些正则引擎中) possessive: { // JavaScript 不直接支持,但可以通过字符类模拟 pattern: /a[^a]*b/, }};5. 常见错误与调试方法
5.1 常见错误
/** * 常见错误示例 */const commonMistakes = { // 1. 忘记转义特殊字符 unescapedDot: /www.example.com/, // 应该为 /www.example.com/ // 2. 使用错误的字符类 wrongCharClass: /[a-zA-Z0-9_]/, // 应该使用 /w/ // 3. 贪婪匹配导致的问题 greedyProblem: /<.*>/, // 会匹配整个字符串,应该使用 /<.*?>/ // 4. 忘记全局标志 noGlobalFlag: 'hello world'.replace(/o/, '0'), // 只替换第一个 // 5. 错误的边界匹配 wrongBoundary: /word/, // 会匹配 "password" 中的 "word"};5.2 调试工具和方法
/** * 正则表达式调试工具 */class RegexDebugger { static test(regex, testCases) { console.log(`测试正则表达式: ${regex}`); console.log('='.repeat(50)); testCases.forEach(testCase => { const result = regex.test(testCase); console.log(`"${testCase}" -> ${result}`); }); } static match(regex, str) { console.log(`在 "${str}" 中匹配 ${regex}`); console.log('='.repeat(50)); const matches = str.match(regex); if (matches) { console.log('匹配结果:', matches); // 显示捕获组 const execResult = regex.exec(str); if (execResult && execResult.length > 1) { console.log('捕获组:'); for (let i = 1; i < execResult.length; i++) { console.log(` 组 ${i}: ${execResult[i]}`); } } } else { console.log('无匹配结果'); } } static stepByStep(regex, str) { console.log(`逐步匹配: ${regex} vs "${str}"`); console.log('='.repeat(50)); let match; const globalRegex = new RegExp(regex.source, regex.flags.includes('g') ? regex.flags : regex.flags + 'g'); while ((match = globalRegex.exec(str)) !== null) { console.log(`找到匹配: "${match[0]}" 在位置 ${match.index}`); console.log(`剩余字符串: "${str.slice(match.index + match[0].length)}"`); } }}// 使用示例const emailRegex = /^[w.-]+@[w.-]+.w+$/;const testEmails = [ '[email protected]', 'invalid.email', '[email protected]', '@example.com', 'user@'];RegexDebugger.test(emailRegex, testEmails);6. 实战项目:表单验证库
让我们综合运用所学知识,创建一个完整的表单验证库:
/** * 表单验证库 */class FormValidator { constructor() { this.rules = new Map(); this.customValidators = new Map(); this.setupDefaultRules(); } setupDefaultRules() { // 内置验证规则 this.rules.set('required', { pattern: /./, message: '此字段为必填项' }); this.rules.set('email', { pattern: /^[w.-]+@[w.-]+.w+$/, message: '请输入有效的邮箱地址' }); this.rules.set('phone', { pattern: /^1[3-9]d{9}$/, message: '请输入有效的手机号码' }); this.rules.set('idCard', { pattern: /^[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]$/, message: '请输入有效的身份证号码' }); this.rules.set('url', { pattern: /^https?://(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/, message: '请输入有效的URL地址' }); this.rules.set('number', { pattern: /^d+(.d+)?$/, message: '请输入有效的数字' }); this.rules.set('integer', { pattern: /^d+$/, message: '请输入有效的整数' }); this.rules.set('chinese', { pattern: /^[u4e00-u9fa5]+$/, message: '只能输入中文' }); this.rules.set('english', { pattern: /^[a-zA-Z]+$/, message: '只能输入英文' }); this.rules.set('postalCode', { pattern: /^d{6}$/, message: '请输入有效的邮政编码' }); } /** * 添加自定义验证规则 */ addRule(name, pattern, message) { this.rules.set(name, { pattern: pattern instanceof RegExp ? pattern : new RegExp(pattern), message: message }); return this; } /** * 添加自定义验证器 */ addValidator(name, validator) { if (typeof validator !== 'function') { throw new Error('验证器必须是函数'); } this.customValidators.set(name, validator); return this; } /** * 验证单个值 */ validate(value, rules) { const errors = []; if (!Array.isArray(rules)) { rules = [rules]; } for (const rule of rules) { const result = this.validateRule(value, rule); if (result !== true) { errors.push(result); } } return { valid: errors.length === 0, errors: errors }; } /** * 验证单个规则 */ validateRule(value, rule) { // 处理必填验证 if (rule === 'required' || (typeof rule === 'object' && rule.type === 'required')) { if (value === null || value === undefined || value === '') { const message = typeof rule === 'object' && rule.message ? rule.message : this.rules.get('required').message; return message; } return true; } // 如果值为空且不是必填项,跳过验证 if (value === null || value === undefined || value === '') { return true; } // 处理内置规则 if (typeof rule === 'string') { const ruleConfig = this.rules.get(rule); if (!ruleConfig) { throw new Error(`未知的验证规则: ${rule}`); } if (!ruleConfig.pattern.test(value)) { return ruleConfig.message; } return true; } // 处理正则表达式 if (rule instanceof RegExp) { if (!rule.test(value)) { return '格式不正确'; } return true; } // 处理对象形式的规则 if (typeof rule === 'object') { // 处理正则表达式规则 if (rule.pattern) { const pattern = rule.pattern instanceof RegExp ? rule.pattern : new RegExp(rule.pattern); if (!pattern.test(value)) { return rule.message || '格式不正确'; } return true; } // 处理自定义验证器 if (rule.validator) { const result = rule.validator(value); if (result !== true) { return result || '验证失败'; } return true; } // 处理内置规则 if (rule.type) { const ruleConfig = this.rules.get(rule.type); if (!ruleConfig) { throw new Error(`未知的验证规则: ${rule.type}`); } if (!ruleConfig.pattern.test(value)) { return rule.message || ruleConfig.message; } return true; } // 处理长度验证 if (rule.minLength !== undefined || rule.maxLength !== undefined) { const length = value.length; if (rule.minLength !== undefined && length < rule.minLength) { return rule.message || `长度不能少于${rule.minLength}个字符`; } if (rule.maxLength !== undefined && length > rule.maxLength) { return rule.message || `长度不能超过${rule.maxLength}个字符`; } return true; } // 处理范围验证 if (rule.min !== undefined || rule.max !== undefined) { const num = Number(value); if (isNaN(num)) { return '请输入有效的数字'; } if (rule.min !== undefined && num < rule.min) { return rule.message || `数值不能小于${rule.min}`; } if (rule.max !== undefined && num > rule.max) { return rule.message || `数值不能大于${rule.max}`; } return true; } } throw new Error(`不支持的验证规则格式`); } /** * 验证整个表单 */ validateForm(formData, schema) { const results = {}; let isValid = true; for (const [field, rules] of Object.entries(schema)) { const value = formData[field]; const result = this.validate(value, rules); results[field] = result; if (!result.valid) { isValid = false; } } return { valid: isValid, fields: results }; }}// 使用示例const validator = new FormValidator();// 添加自定义规则validator.addRule('username', /^[a-zA-Z][a-zA-Z0-9_]{5,17}$/, '用户名必须以字母开头,6-18位,只能包含字母、数字、下划线');// 添加自定义验证器validator.addValidator('passwordMatch', (value, formData) => { return value === formData.password ? true : '两次输入的密码不一致';});// 验证单个值console.log(validator.validate('[email protected]', 'email'));console.log(validator.validate('13800138000', 'phone'));// 验证整个表单const formData = { username: 'john_doe', email: '[email protected]', phone: '13800138000', password: 'Password123!', confirmPassword: 'Password123!', age: 25};const schema = { username: ['required', 'username'], email: ['required', 'email'], phone: ['required', 'phone'], password: [ 'required', { pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*?&])[A-Za-zd@$!%*?&]{8,}$/, message: '密码必须包含大小写字母、数字和特殊字符,至少8位' } ], confirmPassword: [ 'required', { validator: (value, data) => value === data.password ? true : '两次输入的密码不一致' } ], age: [ 'required', { min: 18, max: 100, message: '年龄必须在18-100岁之间' } ]};console.log(validator.validateForm(formData, schema));7. 性能测试与对比
让我们对我们的验证库进行性能测试:
/** * 性能测试 */function performanceTest() { const validator = new FormValidator(); const iterations = 10000; // 测试数据 const testData = { valid_email: '[email protected]', invalid_email: 'invalid.email', valid_phone: '13800138000', invalid_phone: '12345678901', valid_url: 'https://www.example.com', invalid_url: 'not-a-url' }; console.log(`开始性能测试,迭代次数: ${iterations}`); console.log('='.repeat(50)); // 邮箱验证性能测试 console.time('邮箱验证 (valid)'); for (let i = 0; i < iterations; i++) { validator.validate(testData.valid_email, 'email'); } console.timeEnd('邮箱验证 (valid)'); console.time('邮箱验证 (invalid)'); for (let i = 0; i < iterations; i++) { validator.validate(testData.invalid_email, 'email'); } console.timeEnd('邮箱验证 (invalid)'); // 手机号验证性能测试 console.time('手机号验证 (valid)'); for (let i = 0; i < iterations; i++) { validator.validate(testData.valid_phone, 'phone'); } console.timeEnd('手机号验证 (valid)'); console.time('手机号验证 (invalid)'); for (let i = 0; i < iterations; i++) { validator.validate(testData.invalid_phone, 'phone'); } console.timeEnd('手机号验证 (invalid)'); // URL验证性能测试 console.time('URL验证 (valid)'); for (let i = 0; i < iterations; i++) { validator.validate(testData.valid_url, 'url'); } console.timeEnd('URL验证 (valid)'); console.time('URL验证 (invalid)'); for (let i = 0; i < iterations; i++) { validator.validate(testData.invalid_url, 'url'); } console.timeEnd('URL验证 (invalid)');}// 运行性能测试performanceTest();8. 最佳实践总结
8.1 正则表达式编写原则
- 简单明了:尽量使用简单的表达式,避免过度复杂
- 性能优先:考虑回溯和性能影响
- 可读性:添加注释,使用命名捕获组
- 测试覆盖:为每个正则表达式编写测试用例
- 错误处理:提供友好的错误提示
8.2 前端验证策略
- 客户端验证:提供即时反馈,改善用户体验
- 服务端验证:确保数据安全和完整性
- 渐进增强:从基础验证开始,逐步增加复杂度
- 可访问性:确保验证错误对屏幕阅读器友好
8.3 调试技巧
- 使用在线工具:如 regex101.com、RegExr 等
- 逐步构建:从简单模式开始,逐步添加复杂度
- 单元测试:为每个正则表达式编写测试
- 性能分析:使用性能测试工具识别瓶颈
结语
正则表达式是前端开发中的强大工具,掌握它能够显著提升开发效率和代码质量。本文涵盖了从基础到进阶的各个方面,包括表单验证、字符串处理、性能优化等实战场景。
记住,正则表达式的学习是一个循序渐进的过程。建议从简单的模式开始,逐步增加复杂度,并在实际项目中不断练习和应用。同时,也要注意性能影响,避免过度复杂的表达式。
希望这篇文章能够帮助你在前端开发中更好地运用正则表达式,解决实际开发中遇到的各种字符串处理问题。持续学习和实践,你会发现正则表达式变得越来越简单和直观。
参考资料
- MDN Regular Expressions
- JavaScript Regular Expression Tutorial
- RegExr: Learn, Build, & Test RegEx
- Regex101: Online Regex Tester
相关文章
- EpiEvolve自我演化智能体实现流式疫情预测应对制度转变 06-07
- HadSky轻论坛 v8.4.11 正式版 06-07
- 45°C商城系统 V1.3.0 06-07
- PHP得推考试系统 v1.5 06-07
- phpSysInfo v3.4.4 多国语言版 主机系统信息查看 06-07
- AutoCMS全自动建站系统 v1.6 AI生成和自动聚合 06-07