本文详解JavaScript中观察者模式(发布订阅模式)的实现原理。通过自定义PubSub类,演示如何定义观察者、实现事件订阅on、发布emit及取消订阅off方法。结合Nodejs原生EventEmitter与DOM事件机制,提供完整代码示例与测试用例,帮助理解一对多关系及解耦特性。
观察者模式原理与定义
观察者模式,亦称发布订阅模式,定义了一种一对多的依赖关系。在此模式中,多个观察者对象同时监听一个主题对象。当主题对象的状态发生变化时,会自动通知所有注册的观察者。该模式由主题和观察者两类对象构成:主题负责发布事件,观察者通过订阅事件来监控主题。发布者与订阅者之间完全解耦,彼此无需知晓对方的存在,仅通过共享自定义事件名称进行交互。
在Node.js环境中,EventEmitter类已原生提供了对该模式的支持。
在浏览器端的JavaScript中,事件监听机制本质上也是一种观察者模式的应用。开发者可以通过onclick等属性进行事件绑定,随后由交互行为或主动触发来执行相应逻辑。
JavaScript原生PubSub实现
下文将展示一个自定义的PubSub实现,仔细研读该代码有助于深入理解观察者模式的核心机制。
定义观察者类Pubsub
第一步:定义观察者类Pubsub
/* Pubsub */
function Pubsub(){
//存放事件和对应的处理方法
this.handles = {};
}
实现事件订阅on
第二步:实现事件订阅方法on
//传入事件类型type和事件处理handle
on: function (type, handle) {
if(!this.handles[type]){
this.handles[type] = [];
}
this.handles[type].push(handle);
}
实现事件发布emit
第三步:实现事件发布方法emit
emit: function () {
//通过传入参数获取事件类型
var type = Array.prototype.shift.call(arguments);
if(!this.handles[type]){
return false;
}
for (var i = 0; i < this.handles[type].length; i++) {
var handle = this.handles[type][i];
//执行事件
handle.apply(this, arguments);
}
}
需要特别说明的是代码中使用的Array.prototype.shift.call(arguments)。arguments是函数的内置对象,用于获取调用该方法时传入的实参数组。
shift方法的作用是取出数组中的第一个参数,此处即为事件类型type。
实现事件取消订阅off
第四步:实现事件取消订阅方法off
off: function (type, handle) {
handles = this.handles[type];
if(handles){
if(!handle){
handles.length = 0;//清空数组
}else{
for (var i = 0; i < handles.length; i++) {
var _handle = handles[i];
if(_handle === handle){
handles.splice(i,1);
}
}
}
}
}
以下是PubSub类的完整代码实现。
/* Pubsub */
function Pubsub(){
//存放事件和对应的处理方法
this.handles = {};
}
Pubsub.prototype={
//传入事件类型type和事件处理handle
on: function (type, handle) {
if(!this.handles[type]){
this.handles[type] = [];
}
this.handles[type].push(handle);
},
emit: function () {
//通过传入参数获取事件类型
var type = Array.prototype.shift.call(arguments);
if(!this.handles[type]){
return false;
}
for (var i = 0; i < this.handles[type].length; i++) {
var handle = this.handles[type][i];
//执行事件
handle.apply(this, arguments);
}
},
off: function (type, handle) {
handles = this.handles[type];
if(handles){
if(!handle){
handles.length = 0;//清空数组
}else{
for (var i = 0; i < handles.length; i++) {
var _handle = handles[i];
if(_handle === handle){
handles.splice(i,1);
}
}
}
}
}
}
测试用例验证
第五步:测试用例验证
var p1 = new Pubsub();
p1.on('mm', function (name) {
console.log('mm: '+ name);
});
p1.emit('mm','哈哈哈哈');
console.log('===============');
var p2 = new Pubsub();
var fn = function (name) {
console.log('mm2: '+ name);
};
var fn2 = function (name) {
console.log('mm222: '+ name);
};
p2.on('mm2', fn);
p2.on('mm2', fn2);
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
p2.off('mm2', fn);
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
p2.off('mm2');
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');

以上即为JavaScript原生实现观察者模式的完整示例,供参考学习。