用闭包封装验证规则并用高阶函数动态组合,可构建灵活表单校验系统:1. 每条规则是捕获配置参数的工厂函数;2. composefieldvalidator组合多规则返回字段校验器;3. createformvalidator封装表单级校验函数;4. 支持异步规则与条件启用。

用闭包封装验证规则,再通过高阶函数动态组合,是构建灵活表单校验系统的常用方式。核心在于:每条规则是一个返回校验函数的工厂函数,闭包捕获配置参数;组合函数接收多个规则工厂,返回一个能批量执行、聚合结果的统一校验器。
1. 用闭包封装单条验证规则
每条规则(如“必填”、“最小长度”、“邮箱格式”)不直接写死逻辑,而是定义为接受配置参数并返回具体校验函数的工厂函数。闭包让参数在后续调用中持续可用。
- 必填校验:const required = (msg = '此项为必填项') => (value) => value != null && value !== '' || msg;
- 最小长度校验:const minLength = (len, msg = `至少 ${len} 个字符`) => (value) => typeof value === 'string' && value.length >= len || msg;
- 正则匹配校验:const pattern = (reg, msg = '格式不正确') => (value) => reg.test(value) || msg;
2. 组合多个规则成字段级校验器
为某个字段(如 email)一次性应用多条规则,可封装一个组合函数,它接收规则工厂列表,返回一个能按序执行、收集所有错误的函数。
- const composeFieldValidator = (...rules) => (value) => {
const errors = rules.map(ruleFn => ruleFn(value));
return errors.filter(Boolean); // 返回非空错误数组
}; - 使用示例:const emailValidator = composeFieldValidator(
required('邮箱不能为空'),
pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, '请输入有效邮箱'),
minLength(6)
);
console.log(emailValidator('')); // ['邮箱不能为空', '请输入有效邮箱']
3. 进一步封装成表单级统一校验函数
把每个字段的校验器与字段名绑定,再用闭包保存整个规则映射表,最终返回一个接收完整表单数据对象的校验函数,输出结构化错误信息。
- const createFormValidator = (rulesMap) => {
return (formData) => {
const errors = {};
Object.entries(rulesMap).forEach(([field, fieldValidator]) => {
const fieldErrors = fieldValidator(formData[field]);
if (fieldErrors.length > 0) errors[field] = fieldErrors;
});
return errors;
};
}; - 使用示例:const validateForm = createFormValidator({
username: composeFieldValidator(required(), minLength(3)),
email: emailValidator
});
validateForm({ username: 'a', email: 'invalid' });
// → { username: ['至少 3 个字符'], email: ['邮箱不能为空', '请输入有效邮箱'] }
4. 支持异步规则和条件启用(进阶扩展)
闭包同样适用于异步校验(如用户名是否已存在)或依赖其他字段值的条件规则——只需让工厂函数返回 Promise 或接收上下文对象即可。
- const uniqueUsername = (apiCheck, msg = '用户名已被占用') => (value, context) => {
return apiCheck(value).then(exists => !exists || msg);
}; - const when = (condition, ruleFactory) => (value, context) =>
condition(context) ? ruleFactory(value, context) : true; - 例如仅当用户勾选“注册为商家”时才校验营业执照号:when(ctx => ctx.isBusiness, required())
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











