深入理解JavaScript系列(38):设计模式之职责链模式详解
本文详解JavaScript中的职责链模式,通过Handler类原型实现请求传递机制。文章展示了如何构建对象链、拦截处理以及控制调用顺序,并结合DOM事件冒泡机制探讨其实际应用,帮助开发者理解如何解耦请求发送者与接收者。
职责链模式概述
介绍
职责链模式(Chain of responsibility)旨在让多个对象都有机会处理请求,从而避免请求发送者和接收者之间的耦合关系。这些对象被连接成一条链,请求沿着这条链传递,直到有对象处理它为止。
具体而言,请求从链的第一个对象开始传递。链中的每个对象要么亲自处理该请求,要么将其转发给链中的下一个候选者。提交请求的对象并不明确知道具体由哪个对象处理,即存在一个隐式的接受者(implicit receiver)。在运行时刻,任意候选者均可响应请求,且参与链的候选者数量可以是任意的。
JavaScript原型实现
正文
在JavaScript中,我们可以利用其原型特性来实现职责链模式。
代码如下:
var NO_TOPIC = -1;
var Topic;
function Handler(s, t) {
this.successor = s || null;
this.topic = t || 0;
}
Handler.prototype = {
handle: function () {
if (this.successor) {
this.successor.handle()
}
},
has: function () {
return this.topic != NO_TOPIC;
}
};Handler构造函数仅接受两个参数:第一个是继任者(successor,用于将处理请求传递下去),第二个是传递层级(topic,可用于控制在特定层级是否执行操作,也可忽略)。Handler原型暴露了一个handle方法,这是实现该模式的关键。接下来看如何使用上述代码。
代码如下:
var app = new Handler({
handle: function () {
console.log('app handle');
}
}, 3);
var dialog = new Handler(app, 1);
var button = new Handler(dialog, 2);
button.handle();
该代码通过原型特性,调用顺序为button.handle() -> dialog.handle() -> app.handle() -> 参数中的handle()。前三个调用均执行原型中的handle方法,最后才查找并执行传入参数中定义的handle方法并输出结果。这意味着实际上只有最后一层进行了处理。
那么,如何确保在调用时仅由dialog对象进行处理呢?实际上,可以在new button之前定义dialog实例对象的handle方法即可,代码如下:
代码如下:
var app = new Handler({
handle: function () {
console.log('app handle');
}
}, 3);
var dialog = new Handler(app, 1);
dialog.handle = function () {
console.log('dialog before ...')
// 这里做具体的处理操作
console.log('dialog after ...')
};
var button = new Handler(dialog, 2);
button.handle();
该代码的执行结果即为dialog.handle内部的处理结果,而不再是给app传入参数里定义的handle的执行操作。
那能否做到自身处理完以后,再让继任者继续处理呢?答案是肯定的。但在调用handle后,需要利用原型特性调用如下代码:
代码如下:
Handler.prototype.handle.call(this);这句话的意思是调用原型的handle方法,以继续调用其继任者(successor)的handle方法。以下代码表现为:button、dialog、app三个对象定义的handle都会执行。
代码如下:
var app = new Handler({
handle: function () {
console.log('app handle');
}
}, 3);
var dialog = new Handler(app, 1);
dialog.handle = function () {
console.log('dialog before ...')
// 这里做具体的处理操作
Handler.prototype.handle.call(this); //继续往上走
console.log('dialog after ...')
};
var button = new Handler(dialog, 2);
button.handle = function () {
console.log('button before ...')
// 这里做具体的处理操作
Handler.prototype.handle.call(this);
console.log('button after ...')
};
button.handle();
通过代码运行结果可以看出,如果想先自身处理再调用继任者处理,就在末尾执行Handler.prototype.handle.call(this);代码;如果想先处理继任者的代码,就在开头执行Handler.prototype.handle.call(this);代码。
模式应用与总结
总结
职责链模式经常和组合模式一起使用,这样一个构件的父构件可以作为其继任者。
同时,DOM里的事件冒泡机制也与此类似。例如点击一个按钮后,如果不阻止冒泡,其click事件将一直向父元素冒泡。利用这个机制也可以处理很多相关问题,比如本系列设计模式享元模式里的《例1:事件集中管理》的示例代码。
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
