深入理解JavaScript系列(38):设计模式之职责链模式详解

作者:小括号

时间:2026-10-29

浏览:94

本文详解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:事件集中管理》的示例代码。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章