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

作者:代码拾荒时间:2026-10-28 14:21:55浏览:70

本文深入解析 JavaScript 中的观察者模式(又称发布-订阅者模式),通过 DOM 事件、生活案例及游戏开发场景阐述其解耦优势。文章提供了完整的 JavaScript 实现代码,涵盖监听、触发及移除功能,并演示了如何构建一个简单的发布订阅应用,帮助开发者理解事件驱动架构的核心逻辑。

观察者模式概念与 DOM 事件

观察者模式,也被称为发布-订阅者模式,是软件开发中最常用的设计模式之一。它在多种编程语言中得到了广泛应用,例如 JavaScript 中的 DOM 事件机制,本质上就是 JS 与 DOM 之间实现的一种观察者模式。

具体代码示例如下:

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

只要订阅了 div 的 click 事件,当点击 div 时,function 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' 事件的对象都会收到通知。就像之前的面试例子,面试官并不关心面试者收到结果后去哪里吃饭,他只负责把面试者的简历搜集到一起,当面试结果出来时,照着简历上的电话挨个通知。

JavaScript 观察者模式实现

说了这么多概念,下面来看一个具体的实现。实现过程其实很简单:面试者把简历扔到一个盒子里,然后面试官在合适的时机拿着盒子里的简历挨个打电话通知结果。

具体实现代码如下:

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': '麻生希' } )
作者最新文章