职责链模式通过将每个校验规则封装为独立validator实例并用next指针串联,实现新增规则不修改旧逻辑、错误可中断或聚合。核心是validate()显式调用next.handle(),避免隐式return导致流程断裂,支持链式构建与错误累积。

表单校验里嵌套太多 if-else,怎么拆成可维护的链式结构
职责链模式不是为了炫技,而是当校验规则越来越多、分支越来越深时,让新增一个规则不用动已有逻辑。核心是把每个校验项变成独立对象,用 next 指针串起来,失败就中断,成功就传给下一个。
常见错误现象:if (email) { if (isEmail(email)) { if (isNotBanned(email)) { ... } } }——加个手机号校验就得往里塞三层缩进;或者所有校验挤在一个函数里,改邮箱规则时一不小心把密码长度判断删了。
- 每个校验器只关心自己的事:比如
EmailValidator不该知道密码是否符合复杂度 - 必须显式调用
next.handle(),不能靠 return 值隐式流转(否则漏掉某个校验器不会报错,但逻辑就断了) - 建议用类实例而非纯函数,方便后期加状态(比如记录哪个环节失败、累计错误信息)
JavaScript 里怎么写一个轻量职责链,不依赖框架
不需要引入 middleware 库或重写整个表单系统。从最简的 Validator 类开始,每个子类实现 validate() 和 setNext() 就够用。
使用场景:React/Vue 表单提交前、Node.js 接口入参预检、CLI 工具参数解析。
示例关键片段:
class Validator {
next = null;
setNext(nextValidator) {
this.next = nextValidator;
return nextValidator; // 支持链式调用
}
validate(data) {
throw new Error('子类必须实现 validate');
}
}
class EmailValidator extends Validator {
validate(data) {
if (!data.email || !/^.+@.+\..+$/.test(data.email)) {
return { valid: false, field: 'email', message: '邮箱格式不正确' };
}
return this.next ? this.next.validate(data) : { valid: true };
}
}
注意:setNext() 返回 nextValidator 是为了支持 new EmailValidator().setNext(new PhoneValidator()).setNext(...) 这种写法,省去中间变量。
为什么不能直接用数组 forEach 或 reduce 串校验器
因为职责链的关键不是“顺序执行”,而是“可中断”和“责任移交”。forEach 会强行跑完所有项,reduce 容易把错误结果吞掉或误传。
性能影响:链式调用比数组遍历略快一点点(少一次循环开销),但真正重要的是语义清晰——validator.validate(data) 的返回值含义明确:要么当前失败,要么交给下家,没有歧义。
- 用
for...of遍历校验器数组时,一旦 break 就得手动管理索引和状态,容易出错 -
reduce((acc, v) => v.validate(acc) || acc, data)看似简洁,但||会把 falsy 值(比如{ valid: false })当成失败信号跳过后续,实际可能想继续收集所有错误 - 兼容性无问题,ES5+ 全支持,连 IE11 都能跑
多个错误要一起返回?链式结构怎么不丢信息
标准职责链默认“遇到第一个错误就停”,但表单校验常需汇总所有问题。这时候不能改链结构,而是在每个校验器里主动累积错误。
做法是把错误容器(比如 errors = [])作为参数往下传,而不是只传原始 data:
validate(data, errors = []) {
if (!data.email) {
errors.push({ field: 'email', message: '必填' });
} else if (!/^.+@.+\..+$/.test(data.email)) {
errors.push({ field: 'email', message: '格式错误' });
}
return this.next ? this.next.validate(data, errors) : errors;
}
容易踩的坑:errors 必须是引用传递,如果每次新建数组(const errs = []),上层拿不到下家塞的错误;另外别在链头初始化 errors 后直接 return,要确保至少走完一遍链。
复杂点在于:既要保持链的松耦合,又要支持错误聚合。最稳的方式是让每个校验器只负责自己那块错误,不干涉其他环节——这点恰恰是职责链比一堆 if-else 更靠谱的地方。










