需求
在用户信息采集场景中,身份证及各类证件的合法性验证是保障数据准确性的关键环节。本文基于JavaScript实现了一套完整的证件验证工具,不仅覆盖中国大陆居民身份证、台湾居民来往大陆通行证、港澳居民来往内地通行证的核心验证逻辑,还扩展了手机号、邮箱、金额等20+常见数据类型的验证规则。优化后的内容在代码可读性、逻辑严谨性和使用便捷性上均有提升,同时补充了边界场景处理和性能优化建议。
1. 设计思路
1.1 设计原则
- 单一职责:每个函数仅处理一种验证逻辑,便于维护和复用
- 边界覆盖:补充空值、异常格式等边缘场景的处理
- 可扩展性:预留证件类型扩展接口,支持后续新增验证规则
- 性能优化:减少正则表达式回溯,避免不必要的计算
1.2 常量定义
将常量按用途分类,增加注释说明,提升可读性:
javascript
// 中国省/直辖市/自治区/特别行政区代码(共35个)
const PROVINCE_CODES = [
"11", "12", "13", "14", "15", // 华北:京、津、冀、晋、蒙
"21", "22", "23", // 东北:辽、吉、黑
"31", "32", "33", "34", // 华东:沪、苏、浙、皖
"35", "36", "37", // 华东:闽、赣、鲁
"41", "42", "43", "44", // 华中/华南:豫、鄂、湘、粤
"45", "46", // 华南:桂、琼
"50", "51", "52", "53", "54", // 西南:渝、川、黔、滇、藏
"61", "62", "63", "64", "65", // 西北:陕、甘、青、宁、新
"71", "81", "82", "91" // 特殊:台、港、澳、国外
];
// 18位身份证加权因子(根据GB 11643-1999标准)
const ID_CARD_WEIGHTS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 18位身份证校验码映射表(X代表10)
const ID_CARD_CHECK_CODES = ["1", "0", "x", "9", "8", "7", "6", "5", "4", "3", "2"];
// 正则表达式常量(按用途分类)
const REGEX = {
ID_CARD_15: /^\d{15}$/, // 15位身份证(仅数字)
ID_CARD_18: /^\d{17}[\dXx]$/, // 18位身份证(前17位数字,最后一位可X)
TW_CARD: /^\d{8}$/, // 台湾居民来往大陆通行证(8位数字)
HK_MACAO_CARD: /^[HM]\d{8}$/, // 港澳居民来往内地通行证(H/M开头+8位数字)
CHINESE: /^[\u4E00-\u9FA5]+$/, // 纯中文字符(修正原范围,覆盖标准中文)
ENGLISH: /^[a-zA-Z]+$/, // 纯英文字母
ALPHANUMERIC: /^[a-zA-Z0-9]+$/, // 英文+数字组合
ALPHANUMERIC_20: /^[a-zA-Z0-9]{1,20}$/,// 英文+数字(1-20位)
DECIMAL: /^\d+(\.\d+)?$/, // 非负小数(如123、123.45)
INTEGER: /^-?[1-9]\d*$/, // 整数(正负均可,不含前导0)
NATURAL_NUMBER: /^0*[1-9]\d*$/, // 自然数(大于0,允许前导0)
POSITIVE_INTEGER: /^[1-9]\d*$/, // 正整数(不含前导0)
NON_NEGATIVE_INTEGER: /^(0|[1-9]\d*)$/,// 非负整数(0或正整数,不含前导0)
NUMERIC: /^-?\d+(\.\d+)?$/, // 数字(整数或小数,正负均可)
EMAIL: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+\.[a-zA-Z]{2,6}$/, // 邮箱
PHONE: /^1[3-9]\d{9}$/, // 手机号(修正原规则,匹配13-19号段)
TELEPHONE: /^0\d{2,3}-?\d{7,8}$/, // 固定电话(区号0开头+7-8位号码,支持连字符)
POSTCODE: /^\d{6}$/, // 邮政编码(6位数字)
BIRTH_CERT: /^[A-Z][0-9]{9}$/, // 出生医学证明编号(A-Z开头+9位数字)
URL: /^https?:\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?(:\d{1,5})?(\/.*)?$/, // 标准URL(支持http/https,端口,路径)
IP: /^(\d{1,3}\.){3}\d{1,3}$/, // IP地址(格式校验)
AMOUNT: /^\d+(\.\d{1,2})?$/, // 金额(整数或两位小数,如123、123.45)
NO_SPECIAL_SYMBOLS: /^[\u4E00-\u9FA5a-zA-Z0-9_]+$/ // 中文+英文+数字+下划线(无特殊符号)
};
2. 核心验证函数实现
2.1 身份证统一验证入口
新增空值判断和长度校验,避免无效计算:
javascript
/**
* 统一验证所有类型身份证(15位/18位/台湾/港澳)
* @param {string} idCard - 身份证号码(需去除空格)
* @returns {boolean} 验证通过返回true,否则false
*/
function validateIdCard(idCard) {
// 处理空值、空格场景
const trimmedCard = (idCard || "").trim();
if (!trimmedCard) return false;
// 根据长度和格式分发验证逻辑
const length = trimmedCard.length;
if (length === 15) return validate15IdCard(trimmedCard);
if (length === 18) return validate18IdCard(trimmedCard);
if (length === 8 && REGEX.TW_CARD.test(trimmedCard)) return true;
if (length === 9 && REGEX.HK_MACAO_CARD.test(trimmedCard)) return true;
return false;
}
2.2 18位身份证验证(核心逻辑优化)
补充校验码计算 和日期有效性校验(避免2月30日、4月31日等非法日期):
javascript
/**
* 验证18位中国大陆居民身份证
* @param {string} idCard - 18位身份证号码
* @returns {boolean} 验证通过返回true,否则false
*/
function validate18IdCard(idCard) {
// 1. 格式校验(前17位数字,最后一位可X)
if (!REGEX.ID_CARD_18.test(idCard)) return false;
// 2. 省份代码校验
const provinceCode = idCard.substring(0, 2);
if (!PROVINCE_CODES.includes(provinceCode)) return false;
// 3. 出生日期校验(6-14位为YYYYMMDD)
const birthDateStr = idCard.substring(6, 14);
if (!validateBirthDate(birthDateStr)) return false;
// 4. 校验码校验(根据GB 11643-1999标准计算)
return validate18CheckCode(idCard);
}
/**
* 辅助函数:验证出生日期有效性(YYYYMMDD格式)
* @param {string} dateStr - 日期字符串(8位,如20000101)
* @returns {boolean} 有效返回true,否则false
*/
function validateBirthDate(dateStr) {
// 格式长度校验
if (dateStr.length !== 8) return false;
// 提取年、月、日(月份从0开始,需减1)
const year = parseInt(dateStr.substring(0, 4), 10);
const month = parseInt(dateStr.substring(4, 6), 10) - 1;
const day = parseInt(dateStr.substring(6, 8), 10);
// 构建日期对象并反向验证(避免非法日期如20240230)
const date = new Date(year, month, day);
return (
date.getFullYear() === year &&
date.getMonth() === month &&
date.getDate() === day &&
year >= 1900 && // 身份证出生日期不会早于1900年
year <= new Date().getFullYear() // 不会晚于当前年份
);
}
/**
* 辅助函数:计算18位身份证校验码并验证
* @param {string} idCard - 18位身份证号码
* @returns {boolean} 校验通过返回true,否则false
*/
function validate18CheckCode(idCard) {
// 提取前17位数字并转换为数组
const front17Digits = idCard.substring(0, 17).split("").map(Number);
// 计算加权和(前17位数字 × 对应权重)
const weightedSum = front17Digits.reduce((sum, digit, index) => {
return sum + digit * ID_CARD_WEIGHTS[index];
}, 0);
// 计算预期校验码
const expectedCheckCode = ID_CARD_CHECK_CODES[weightedSum % 11];
// 比较实际校验码(忽略大小写)
const actualCheckCode = idCard.substring(17).toLowerCase();
return actualCheckCode === expectedCheckCode;
}
2.3 15位身份证验证(兼容历史格式)
15位身份证无校验码,需补充年份补全逻辑(默认19xx年):
javascript
/**
* 验证15位中国大陆居民身份证(历史格式,2013年起停止发放)
* @param {string} idCard - 15位身份证号码
* @returns {boolean} 验证通过返回true,否则false
*/
function validate15IdCard(idCard) {
// 1. 格式校验
if (!REGEX.ID_CARD_15.test(idCard)) return false;
// 2. 省份代码校验
const provinceCode = idCard.substring(0, 2);
if (!PROVINCE_CODES.includes(provinceCode)) return false;
// 3. 出生日期校验(6-12位为YYMMDD,需补全为YYYYMMDD)
const birthYear = `19${idCard.substring(6, 8)}`; // 15位身份证年份默认19xx
const birthMonthDay = idCard.substring(8, 12);
const birthDateStr = `${birthYear}${birthMonthDay}`;
return validateBirthDate(birthDateStr);
}
2.4 港澳台证件验证(独立函数)
明确函数命名,补充注释说明证件类型:
javascript
/**
* 验证台湾居民来往大陆通行证(台胞证)
* @param {string} card - 台胞证号码(8位数字,如12345678)
* @returns {boolean} 验证通过返回true,否则false
*/
function validateTWCard(card) {
const trimmedCard = (card || "").trim();
return REGEX.TW_CARD.test(trimmedCard);
}
/**
* 验证港澳居民来往内地通行证(港澳通行证)
* @param {string} card - 港澳通行证号码(H/M开头+8位数字,如H12345678)
* @returns {boolean} 验证通过返回true,否则false
*/
function validateHKMacaoCard(card) {
const trimmedCard = (card || "").trim();
return REGEX.HK_MACAO_CARD.test(trimmedCard);
}
3. 扩展验证规则(20+常见类型)
将扩展规则按数据类型分类,每个函数补充使用场景说明,便于快速查找:
3.1 字符类型验证
javascript
/**
* 验证是否为纯中文字符(如姓名)
* @param {string} value - 输入值
* @returns {boolean} 纯中文返回true,否则false
*/
function isChinese(value) {
const trimmedValue = (value || "").trim();
return REGEX.CHINESE.test(trimmedValue);
}
/**
* 验证是否为纯英文字符(如英文名)
* @param {string} value - 输入值
* @returns {boolean} 纯英文返回true,否则false
*/
function isEnglish(value) {
const trimmedValue = (value || "").trim();
return REGEX.ENGLISH.test(trimmedValue);
}
/**
* 验证是否为英文+数字组合(1-20位,如用户名)
* @param {string} value - 输入值
* @returns {boolean} 符合规则返回true,否则false
*/
function isAlphanumericWithLimit(value) {
const trimmedValue = (value || "").trim();
return REGEX.ALPHANUMERIC_20.test(trimmedValue);
}
/**
* 验证是否为英文+数字组合(无长度限制,如设备编号)
* @param {string} value - 输入值
* @returns {boolean} 符合规则返回true,否则false
*/
function isAlphanumeric(value) {
const trimmedValue = (value || "").trim();
return REGEX.ALPHANUMERIC.test(trimmedValue);
}
3.2 数字类型验证
javascript
/**
* 验证是否为非负小数(如身高、体重)
* @param {string} value - 输入值(需转为字符串验证)
* @returns {boolean} 非负小数返回true,否则false
*/
function isDecimal(value) {
const trimmedValue = (value || "").trim();
return REGEX.DECIMAL.test(trimmedValue);
}
/**
* 验证是否为整数(正负均可,如年龄差)
* @param {string} value - 输入值(需转为字符串验证)
* @returns {boolean} 整数返回true,否则false
*/
function isInteger(value) {
const trimmedValue = (value || "").trim();
return REGEX.INTEGER.test(trimmedValue);
}
/**
* 验证是否为正整数(不含前导0,如数量)
* @param {string} value - 输入值(需转为字符串验证)
* @returns {boolean} 正整数返回true,否则false
*/
function isPositiveInteger(value) {
const trimmedValue = (value || "").trim();
return REGEX.POSITIVE_INTEGER.test(trimmedValue);
}
/**
* 验证是否为非负整数(0或正整数,如评分)
* @param {string} value - 输入值(需转为字符串验证)
* @returns {boolean} 非负整数返回true,否则false
*/
function isNonNegativeInteger(value) {
const trimmedValue = (value || "").trim();
return REGEX.NON_NEGATIVE_INTEGER.test(trimmedValue);
}
/**
* 验证是否为金额格式(整数或两位小数,如订单金额)
* @param {string} value - 输入值(需转为字符串验证)
* @returns {boolean} 符合金额格式返回true,否则false
*/
function isAmount(value) {
const trimmedValue = (value || "").trim();
return REGEX.AMOUNT.test(trimmedValue);
}
3.3 通讯信息验证
javascript
/**
* 验证是否为手机号(支持13-19号段,如13800138000)
* @param {string} value - 输入值(需去除空格和连字符)
* @returns {boolean} 符合手机号格式返回true,否则false
*/
function isPhoneNumber(value) {
const trimmedValue = (value || "").trim().replace(/-/g, ""); // 移除可能的连字符
return REGEX.PHONE.test(trimmedValue);
}
/**
* 验证是否为固定电话(如010-12345678、02161234567)
* @param {string} value - 输入值
* @returns {boolean} 符合固定电话格式返回true,否则false
*/
function isLandlineNumber(value) {
const trimmedValue = (value || "").trim();
return REGEX.TELEPHONE.test(trimmedValue);
}
/**
* 验证是否为手机号或固定电话(兼容两种格式)
* @param {string} value - 输入值
* @returns {boolean} 符合任一格式返回true,否则false
*/
function isContactNumber(value) {
const trimmedValue = (value || "").trim();
return isPhoneNumber(trimmedValue) || isLandlineNumber(trimmedValue);
}
/**
* 验证是否为邮箱地址(支持常见格式,如user.name+tag@domain.co.uk)
* @param {string} value - 输入值
* @returns {boolean} 符合邮箱格式返回true,否则false
*/
function isEmail(value) {
const trimmedValue = (value || "").trim().toLowerCase();
return REGEX.EMAIL.test(trimmedValue);
}
/**
* 验证是否为邮政编码(6位数字,如100000)
* @param {string} value - 输入值
* @returns {boolean} 符合邮政编码格式返回true,否则false
*/
function isPostcode(value) {
const trimmedValue = (value || "").trim();
return REGEX.POSTCODE.test(trimmedValue);
}
3.4 网络信息验证
javascript
/**
* 验证是否为URL地址(支持http/https,含端口和路径,如https://example.com:8080/path)
* @param {string} value - 输入值
* @returns {boolean} 符合URL格式返回true,否则false
*/
function isURL(value) {
const trimmedValue = (value || "").trim();
return REGEX.URL.test(trimmedValue);
}
/**
* 验证是否为IP地址(IPv4,如192.168.1.1,排除256+非法值)
* @param {string} value - 输入值
* @returns {boolean} 合法IPv4地址返回true,否则false
*/
function isIPAddress(value) {
const trimmedValue = (value || "").trim();
// 先校验格式,再验证每个段是否在0-255之间
if (!REGEX.IP.test(trimmedValue)) return false;
const ipSegments = trimmedValue.split(".").map(Number);
return ipSegments.every(segment => segment >= 0 && segment <= 255);
}
3.5 特殊证件与格式验证
javascript
/**
* 验证是否为出生医学证明编号(A-Z开头+9位数字,如A123456789)
* @param {string} value - 输入值
* @returns {boolean} 符合编号格式返回true,否则false
*/
function isBirthCertificateNumber(value) {
const trimmedValue = (value || "").trim();
return REGEX.BIRTH_CERT.test(trimmedValue);
}
/**
* 验证是否不含特殊符号(仅允许中文、英文、数字、下划线,如用户名、备注)
* @param {string} value - 输入值
* @returns {boolean} 无特殊符号返回true,否则false
*/
function isWithoutSpecialSymbols(value) {
const trimmedValue = (value || "").trim();
return REGEX.NO_SPECIAL_SYMBOLS.test(trimmedValue);
}
4. 工具使用示例(完整场景)
以下示例覆盖证件验证、用户信息校验、表单提交前检查等真实场景,方便直接参考:
javascript
// 1. 身份证验证(18位、15位、港澳台证件)
console.log(validateIdCard("11010519491231002X")); // true(18位合法身份证)
console.log(validateIdCard("110105491231002")); // true(15位合法身份证)
console.log(validateTWCard("12345678")); // true(台湾通行证)
console.log(validateHKMacaoCard("H12345678")); // true(香港通行证)
// 2. 用户信息校验(姓名、手机号、邮箱)
console.log(isChinese("张三")); // true(纯中文姓名)
console.log(isPhoneNumber("13800138000")); // true(合法手机号)
console.log(isEmail("user.name+tag@example.com")); // true(合法邮箱)
console.log(isPostcode("100000")); // true(合法邮编)
// 3. 表单数据校验(金额、数量、备注)
console.log(isAmount("99.99")); // true(合法金额)
console.log(isPositiveInteger("10")); // true(合法数量)
console.log(isWithoutSpecialSymbols("备注_123")); // true(无特殊符号备注)
// 4. 网络信息校验(URL、IP)
console.log(isURL("https://example.com:8080/path"));// true(合法URL)
console.log(isIPAddress("192.168.1.255")); // true(合法IPv4)
console.log(isIPAddress("256.0.0.1")); // false(非法IP段)
5. 优化
5.1 性能优化
- 缓存正则表达式 :本文已将正则存入
REGEX
常量,避免重复创建,提升高频调用效率。 - 提前终止无效校验 :如身份证验证中,若格式不匹配直接返回
false
,无需执行后续省份、日期校验。 - 避免不必要的转换 :对数字类验证(如金额、整数),若输入为
number
类型,可先转为字符串再校验(避免typeof
判断冗余)。
5.2 功能扩展
- 支持更多证件类型 :可新增护照(如
/^[EeKkGgDdSsPpHh]\d{8}$/
)、军官证等验证规则,补充到validateIdCard
入口函数。 - 添加错误信息反馈 :将返回值从
boolean
改为对象(如{ valid: true, message: "验证通过" }
),方便前端提示用户具体错误原因(如"省份代码无效""出生日期非法")。 - 适配国际化场景 :若项目涉及海外用户,可扩展英文姓名验证(支持空格、连字符,如
/^[a-zA-Z\s-]+$/
)、国际手机号验证(如/^\+[1-9]\d{1,14}$/
,符合E.164标准)。
5.3 错误处理
- 空值与空格处理 :所有函数均先对输入值
trim()
,避免用户误输入空格导致验证失败。 - 异常值捕获 :若输入为
null/undefined/NaN
,通过(value || "")
转为空字符串,避免校验过程中报错。 - 日期合理性增强 :可补充"年龄不超过150岁""未来日期无效"等逻辑(如
year >= new Date().getFullYear() - 150
),进一步提升身份证验证严谨性。
6. 总结
可直接将工具封装为validation-utils.js
文件,在前端表单、后端接口参数校验等场景中复用,有效减少错误数据录入,提升系统数据质量。