观察者模式 ( 又叫发布者-订阅者模式 ) 应该是最常用的模式之一. 在很多语言里都得到大量应用. 包括我们平时接触的 dom 事件. 也是 js 和 dom 之间实现的一种观察者模式.
1 2 3 4 5 |
div.onclick = function click (){ alert ( ''click' ) } |
只要订阅了 div 的 click 事件. 当点击 div 的时候, function click 就会被触发.
那么到底什么是观察者模式呢. 先看看生活中的观察者模式。
好莱坞有句名言. “不要给我打电话, 我会给你打电话”. 这句话就解释了一个观察者模式的来龙去脉。 其中 “我” 是发布者, “你” 是订阅者。
再举个例子,我来公司面试的时候,完事之后每个面试官都会对我说:“请留下你的联系方式, 有消息我们会通知你”。 在这里 “我” 是订阅者, 面试官是发布者。所以我不用每天或者每小时都去询问面试结果, 通讯的主动权掌握在了面试官手上。而我只需要提供一个联系方式。
观察者模式可以很好的实现 2 个模块之间的解耦。 假如我正在一个团队里开发一个 html5 游戏. 当游戏开始的时候,需要加载一些图片素材。加载好这些图片之后开始才执行游戏逻辑. 假设这是一个需要多人合作的项目. 我完成了 Gamer 和 Map 模块, 而我的同事 A 写了一个图片加载器 loadImage.
loadImage 的代码如下
1 2 3 4 5 6 7 |
loadImage( imgAry, function(){ Map.init(); Gamer.init(); } ) |
当图片加载好之后, 再渲染地图, 执行游戏逻辑. 嗯, 这个程序运行良好. 突然有一天, 我想起应该给游戏加上声音功能. 我应该让图片加载器添上一行代码.
1 2 3 4 5 6 7 8 9 |
loadImage( imgAry, function(){ Map.init(); Gamer.init(); Sount.init(); } ) |
可是写这个模块的同事 A 去了外地旅游. 于是我打电话给他, 喂. 你的 loadImage 函数在哪, 我能不能改一下, 改了之后有没有副作用. 如你所想, 各种不淡定的事发生了. 如果当初我们能这样写呢:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
loadImage.listen( ''ready', function(){ Map.init(); }) loadImage.listen( ''ready', function(){ Gamer.init(); }) loadImage.listen( ''ready', function(){ Sount.init(); }) |
loadImage 完成之后, 它根本不关心将来会发生什么, 因为它的工作已经完成了. 接下来它只要发布一个信号.
loadImage.trigger( ''ready' );
那么监听了 loadImage 的'ready'事件的对象都会收到通知. 就像上个面试的例子. 面试官根本不关心面试者们收到面试结果后会去哪吃饭. 他只负责把面试者的简历搜集到一起. 当面试结果出来时照着简历上的电话挨个通知.
说了这么多概念, 来一个具体的实现. 实现过程其实很简单. 面试者把简历扔到一个盒子里, 然后面试官在合适的时机拿着盒子里的简历挨个打电话通知结果.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
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 } } |
最后用观察者模式来做一个成人电视台的小应用.
//订阅者
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var adultTv = Event(); adultTv .listen( ''play', function( data ){ alert ( "今天是谁的电影" + data.name ); }); //发布者 adultTv .trigger( ''play', { 'name': '麻生希' } ) |
soon 2017 年 11 月 10 日
分享一个 0.9K 的订阅器 onfire.js
看一遍源码加深理解
https://github.com/hustcc/onfire.js
Shock 2016 年 11 月 17 日
if ( fn.apply( __this, arguments ) === false) { return false; } 这个代码块为什么要判断是否返回 false? 就这里没看懂。
极乐网 2016 年 9 月 12 日
赞!能转到我的网站吗?http://www.dreawer.com
太空漫步 2016 年 9 月 7 日
原来作者是麻生希的粉丝,可以可以,哈哈
vicvinc 2016 年 5 月 25 日
666666666666
康伟意 2016 年 1 月 27 日
嘿嘿,回个贴表明我来过。
苏巧容 2016 年 1 月 22 日
必须得顶呀!
刘思海 2016 年 1 月 18 日
onClick 方法感觉应该是委托模式
啊啊 2015 年 11 月 26 日
不能这样,,,obj[key]||[] 不是数组
aa 2015 年 11 月 26 日
不行,obj[key] 不是数组了
js mutationobserver简要介绍 — 好JSER 2015 年 7 月 17 日
[…] 一些好的文章观察者模式与委托模式的区别深入理解 JavaScript 系列(32):设计模式之观察者模式【Javascript 设计模式 3】-观察者模式 […]
无量光 2013 年 10 月 23 日
这一大陀看得我蛋都碎了:
listen = function( key, eventfn ) { //把简历扔盒子, key 就是联系方式.
var stack, _ref; //stack 是盒子
stack = ( _ref = obj[key] ) != null ? _ref : obj[ key ] = [];
return stack.push( eventfn );
};
就不能这样?
listen = function( key, eventfn ) { //把简历扔盒子, key 就是联系方式.
return (obj[key]||[]).push( eventfn );
};
svenzeng 2013 年 10 月 24 日
可以
姓称名呼 2015 年 6 月 11 日
就是
cheney 2013 年 8 月 25 日
最后那陀代码是用 coffee 编译出来的么?看着有些蛋疼,如果可以的话建议楼主直接上 coffee,简洁明了
svenzeng 2013 年 10 月 24 日
确实 coffee 编译的 :)
finley 2013 年 8 月 20 日
哈哈 麻生希都出来了 ,喜欢楼主的风格。很多代码看不懂什么意思,建议楼主出本书,讲的再细致些我必买
【Javascript设计模式1】-单例模式 | Tencent AlloyTeam 2012 年 11 月 28 日
[…] 观察者模式 […]
振鹏 2012 年 11 月 7 日
如果能够阐述使用观察者模式和不实用观察者解决现实中问题的区别,这个会更加形象一点 = =
niko 2012 年 10 月 27 日
额,缩进看着蛋疼=。=
steven0lisa 2012 年 10 月 25 日
第一段代码写多了一个引号
【Javascript设计模式12】-迭代器模式 | Tencent AlloyTeam 2012 年 10 月 25 日
[…] 观察者模式 […]