当前位置: 首页 > news >正文

前端正则表达式实战合集:表单验证与字符串处理高频场景

前端正则表达式实战合集:表单验证与字符串处理高频场景

文章目录

  • 前端正则表达式实战合集:表单验证与字符串处理高频场景
    • 1. 正则表达式基础回顾
      • 1.1 基本概念
      • 1.2 常用元字符
    • 2. 表单验证场景实战
      • 2.1 邮箱验证
      • 2.2 手机号验证
      • 2.3 身份证号验证
      • 2.4 密码强度验证
      • 2.5 URL验证
      • 2.6 IP地址验证
    • 3. 字符串处理高频场景
      • 3.1 HTML标签清理
      • 3.2 特殊字符过滤
      • 3.3 数字格式化
      • 3.4 字符串提取
      • 3.5 文本替换
    • 4. 性能优化技巧
      • 4.1 避免回溯灾难
      • 4.2 预编译正则表达式
      • 4.3 使用合适的量词
    • 5. 常见错误与调试方法
      • 5.1 常见错误
      • 5.2 调试工具和方法
    • 6. 实战项目:表单验证库
    • 7. 性能测试与对比
    • 8. 最佳实践总结
      • 8.1 正则表达式编写原则
      • 8.2 前端验证策略
      • 8.3 调试技巧
    • 结语
    • 参考资料

正则表达式是前端开发中不可或缺的工具,掌握它能让你的表单验证和字符串处理工作事半功倍。本文将带你深入实战,从基础到进阶,全面掌握前端开发中的正则表达式应用。

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('user@example.com')); // true
console.log(validateEmailBasic('user.name@example.com')); // true
console.log(validateEmailBasic('user@example.co.uk')); // true
console.log(validateEmailBasic('invalid.email')); // false

2.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')); // true
console.log(validateChinesePhone('+8613800138000')); // false
console.log(validatePhoneWithAreaCode('+8613800138000')); // true
console.log(validateLandline('010-12345678')); // true

2.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')); // true
console.log(validateIDCard('110105900307283')); // true
console.log(validateIDCard('123456789012345')); // false

2.4 密码强度验证

密码强度验证通常需要满足多个条件:

/*** 密码强度验证 - 基础版本*/
function validatePasswordBasic(password) {// 至少8位,包含字母和数字const regex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/;return regex.test(password);
}/*** 密码强度验证 - 中级版本* 必须包含大小写字母、数字、特殊字符中的至少3种*/
function validatePasswordMedium(password) {const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{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-z\\d${specialChars.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]{${minLength},${maxLength}}$`;const regex = new RegExp(pattern);return regex.test(password);
}// 测试用例
console.log(validatePasswordBasic('password123')); // true
console.log(validatePasswordMedium('Password123!')); // true
console.log(validatePasswordAdvanced('mypassword', {requireUppercase: false})); // true

2.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')); // true
console.log(validateURLBasic('http://example.com/path/to/page')); // true
console.log(extractDomain('https://www.example.com/path')); // www.example.com

2.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')); // true
console.log(validateIPv4('256.1.1.1')); // false
console.log(validateIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')); // true

3. 字符串处理高频场景

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 = {'&': '&amp;','<': '&lt;','>': '&gt;','"': '&quot;',"'": '&#39;'};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-\x1F\x7F-\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]|\b\w)/g, (word, index) => {return index === 0 ? word.toLowerCase() : word.toUpperCase();}).replace(/\s+/g, '');case 'pascal':return str.replace(/(?:^\w|[A-Z]|\b\w)/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('user@example.com')); // true

4.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 = ['user@example.com','invalid.email','user.name@example.co.uk','@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('user@example.com', 'email'));
console.log(validator.validate('13800138000', 'phone'));// 验证整个表单
const formData = {username: 'john_doe',email: 'john@example.com',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-z\d@$!%*?&]{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: 'user@example.com',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 正则表达式编写原则

  1. 简单明了:尽量使用简单的表达式,避免过度复杂
  2. 性能优先:考虑回溯和性能影响
  3. 可读性:添加注释,使用命名捕获组
  4. 测试覆盖:为每个正则表达式编写测试用例
  5. 错误处理:提供友好的错误提示

8.2 前端验证策略

  1. 客户端验证:提供即时反馈,改善用户体验
  2. 服务端验证:确保数据安全和完整性
  3. 渐进增强:从基础验证开始,逐步增加复杂度
  4. 可访问性:确保验证错误对屏幕阅读器友好

8.3 调试技巧

  1. 使用在线工具:如 regex101.com、RegExr 等
  2. 逐步构建:从简单模式开始,逐步添加复杂度
  3. 单元测试:为每个正则表达式编写测试
  4. 性能分析:使用性能测试工具识别瓶颈

结语

正则表达式是前端开发中的强大工具,掌握它能够显著提升开发效率和代码质量。本文涵盖了从基础到进阶的各个方面,包括表单验证、字符串处理、性能优化等实战场景。

记住,正则表达式的学习是一个循序渐进的过程。建议从简单的模式开始,逐步增加复杂度,并在实际项目中不断练习和应用。同时,也要注意性能影响,避免过度复杂的表达式。

希望这篇文章能够帮助你在前端开发中更好地运用正则表达式,解决实际开发中遇到的各种字符串处理问题。持续学习和实践,你会发现正则表达式变得越来越简单和直观。

参考资料

  • MDN Regular Expressions
  • JavaScript Regular Expression Tutorial
  • RegExr: Learn, Build, & Test RegEx
  • Regex101: Online Regex Tester

http://www.dtcms.com/a/585488.html

相关文章:

  • 嵌入式Linux驱动开发全流程:工具协作+核心概念拆解(从入门到理解)
  • 建设通是正规网站吗洛阳市霞光游乐设备有限公司
  • 鸿蒙(HarmonyOS)开发常见错误分析与解决方案
  • 入门git:部署到公网
  • Vue 4.0实战指南:从数据驱动到状态管理的核心突破
  • 人工智能:什么是AIGC?什么是AI4S?人工智能四大核心领域全景解析
  • Git 开发全流程规范:分支创建+关联远程+rebase同步+分支清理实战
  • 【小程序】详细比较微信小程序的 onLoad 和 onShow
  • Linux文件系统简介
  • 人工智能:卫星网络的“智慧中枢“
  • 网站底部导航菜单自己搞网站建设
  • 百度测开面经(分类版)
  • 回归、分类、聚类
  • 【Linux网络】Socket编程TCP-实现Echo Server(上)
  • 关系型数据库-PostgreSQL
  • 英文网站定制哪家好wordpress上传主题提示要ftp
  • Vue 项目实战《尚医通》,已有医院数据的 TS 类型定义,笔记12
  • UE5 C++ 进阶学习 —— 02 - 小案例
  • Linux的waitpid函数:深入解析与应用实践
  • 历史数据分析——洛阳钼业
  • MySQL EXPLAIN 详解与优化指南
  • ADB 无线调试 APP 完全攻略(2025 最新版)—— 从连接到查看日志,一文搞定!
  • 商家入驻网站建设免费网站怎么做
  • C语言数据结构之堆
  • VIVO算法/大模型面试题及参考答案
  • 临海网站制作好了如何上线网站开发的要求
  • KingbaseES:从MySQL兼容到权限隔离与安全增强的跨越
  • 网站改版竞品分析怎么做可以先做网站再开公司吗
  • Go语言基础:语言特性、语法基础与数据类型
  • 解决 PyQt5 中 sipPyTypeDict() 弃用警告的完整指南