JavaScript观察者模式详解:从概念到实战实现

作者:拾光

时间:2026-10-29

浏览:405

本文深入解析JavaScript中的观察者模式(又称发布-订阅模式),阐述其作为行为型设计模式的核心作用:当对象状态改变时自动通知关联对象。文章通过生活案例与游戏开发场景说明其解耦优势,详细定义Subject与Observer角色,并提供完整的原生JS实现代码及实际应用示例,帮助开发者理解并应用该模式优化代码结构。

文章目录

观察者模式概述

观察者模式(Observer Pattern)是GoF(Gang of Four)提出的23种经典软件设计模式之一。作为一种行为型模式,其核心功能在于:当某个对象的状态发生变动时,能够自动通知所有与之关联的对象,从而触发这些关联对象的自动刷新或状态更新。

观察者模式的基本概念

作为行为型设计模式的一种,观察者模式旨在解决对象间状态同步的问题。它通过提供一种同步通信机制,确保当被观察对象的状态发生变化时,所有依赖该对象的关联对象都能保持状态的一致性。

观察者模式的角色

观察者模式包含以下主要角色:

Subject(被观察者/主题):这是被观察的目标对象。当Subject的状态发生变化时,它需要通知队列中所有的观察者对象。Subject负责维护一个观察者对象的列表,并提供添加、删除以及通知观察者的功能。 ConcreteSubject(具体被观察者):这是被观察者的具体实现类,通常包含具体的属性状态以及其他业务操作。 Observer(观察者):这是一个接口或抽象类。当Subject的状态发生变化时,Observer对象会通过一个回调函数(callback)接收到通知。 ConcreteObserver(具体观察者):这是观察者的具体实现类。在接收到通知后,它将执行具体的业务逻辑处理。

生活中的观察者模式

观察者模式,又被称为发布者-订阅者模式(Publisher-Subscriber Pattern),是软件开发中最常用的设计模式之一。它在多种编程语言中都有广泛应用,包括JavaScript中的DOM事件机制,这也是JS与DOM之间实现的一种典型的观察者模式。


div.onclick = function click (){
alert ( ”click' )
}

例如,只要为div元素订阅了click事件,当用户点击该div时,绑定的click函数就会被触发执行。

那么,究竟什么是观察者模式?我们可以通过生活中的场景来理解。

好莱坞有一句名言:“不要给我打电话,我会给你打电话。”这句话生动地解释了观察者模式的运作机制。在这个比喻中,“我”扮演发布者的角色,而“你”则是订阅者。

再举一个面试的例子:当我完成公司面试后,每位面试官都会对我说:“请留下你的联系方式,有消息我们会通知你。”在这里,“我”是订阅者,而面试官是发布者。因此,我不需要每天或每小时去询问面试结果,通讯的主动权掌握在面试官手中,而我只需要提供联系方式即可。

观察者模式的解耦优势

观察者模式能够很好地实现两个模块之间的解耦。假设我正在一个团队中开发一款HTML5游戏,游戏开始时需要加载一些图片素材,加载完成后才执行游戏逻辑。这是一个需要多人合作的项目,我负责完成Gamer和Map模块,而我的同事A编写了一个图片加载器loadImage。

loadImage的初始代码如下:


loadImage( imgAry, function(){
Map.init();
Gamer.init();
} )

这段代码在图片加载完成后,渲染地图并执行游戏逻辑,程序运行良好。然而,某天我突然想到应该给游戏添加声音功能,于是需要让图片加载器增加一行代码来初始化声音模块。


loadImage( imgAry, function(){
Map.init();
Gamer.init();
Sount.init();
} )

但是,编写该模块的同事A正在外地旅游。如果我打电话给他,询问loadImage函数的位置,能否修改代码,以及修改后是否有副作用,可能会引发各种不愉快的情况。如果当初我们采用以下方式编写代码,情况会如何呢?


loadImage.listen( ”ready', function(){
Map.init();
})
loadImage.listen( ”ready', function(){
Gamer.init();
})
loadImage.listen( ”ready', function(){
Sount.init();
})

在loadImage完成工作后,它并不关心将来会发生什么,因为它的任务已经完成。接下来,它只需要发布一个信号。


loadImage.trigger( ”ready' );

此时,所有监听了loadImage的'ready'事件的对象都会收到通知。这就像之前的面试例子:面试官并不关心面试者收到结果后去哪里吃饭,他只负责收集简历,并在结果出来后,按照简历上的电话逐一通知。

观察者模式的代码实现

概念讲解完毕,下面来看一个具体的实现。实现过程其实很简单:就像面试者把简历扔进一个盒子里,面试官在合适的时机拿着盒子里的简历逐一打电话通知结果。


Events = function() {
var listen, log, obj, one, remove, trigger, __this;
obj = {};
__this = this;
listen = function( key, eventfn ) { //把简历扔盒子, key就是联系方式.
var stack, _ref; //stack是盒子
stack = ( _ref = obj[key] ) != null ? _ref : obj[ key ] = [];
return stack.push( eventfn );
};
one = function( key, eventfn ) {
remove( key );
return listen( key, eventfn );
};
remove = function( key ) {
var _ref;
return ( _ref = obj[key] ) != null ? _ref.length = 0 : void 0;
};
trigger = function() { //面试官打电话通知面试者
var fn, stack, _i, _len, _ref, key;
key = Array.prototype.shift.call( arguments );
stack = ( _ref = obj[ key ] ) != null ? _ref : obj[ key ] = [];
for ( _i = 0, _len = stack.length; _i < _len; _i++ ) {
fn = stack[ _i ];
if ( fn.apply( __this, arguments ) === false) {
return false;
}
}
return {
listen: listen,
one: one,
remove: remove,
trigger: trigger
}
}

最后,我们用观察者模式来实现一个成人电视台的小应用。


//订阅者
var adultTv = Event();
adultTv .listen( ”play', function( data ){
alert ( “今天是谁的电影” + data.name );
});
//发布者
adultTv .trigger( ”play', { ‘name': ‘麻生希' } )

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章