圣诞主题也少量运用一些设计模式,结合下实际的代码,让大家对模式的实际运用有一定的理解。这里主要讲解下最常见"观察者模式"的概念,并且在后面的代码会实际运用到
理论上说,观察者模式又叫发布-订阅模式,就是定义一个一对多的依赖关系,当一个对象的状态发生改变,所依赖它的对象将会得到这个改变的通知。在JS的实现级上,一般通过事件的方式模拟发布-订阅模式
简单的来说,我们可以这样想下,去商场买衣服,刚好断货了,特别喜欢的话就会留下电话,等待卖家通知,所以就是一个典型的发布-订阅的关系,买家不需要每天去询问是否到货,卖家到后才会主动通知
说了半天,在JS中用这个东东具体干嘛呢? 设计模式的核心就是为了解耦,解耦,解耦重要事说三遍。通过这个模式,一个对象可以不再显示的调用另外一个对象的接口了,或者不关心对象的内部运行状态,只对感兴趣的事件进行观察了,具体用法后续的页面切换会讲解到,这里先理解下如何实现这个模式的简单逻辑。
JS里对观察者模式的实现是通过回调来实现的,参考右边的代码observer.js,其内部包含了3个方法:订阅、退订、发布也就是对应的bind、unbind、trigger方法
创建一个观察者对象
var observer = new Observer();
通过调用subscribe方法,实现一个事件的观察
observer.subscribe("任务名",处理函数)
通过publish触发观察事件,在任意时刻触发了这个观察的任务名,将会触发这个事件订阅subscribe方法
observer.publish("任务名")
取消事件订阅,意味着就不会执行了
observer.unsubscribe("任务名")
观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。概念是有一点抽象的,等到后面实际结合理解。
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <title>圣诞主题</title> <script src="observer.js"></script> </head> <body> <button>点击触发page任务</button> <button>点击取消page任务</button> <script type="text/javascript"> //观察者 var observer = new Observer(); //page函数 //模拟运行1秒的一个函数 function page() { setTimeout(function() { //发布一个完成的时间 observer.publish("compelte") }, 1000) } //订阅一个完成的时间 observer.subscribe("compelte", function() { alert("page任务完成") }) //触发任务 document.querySelectorAll("button")[0].addEventListener("click", function() { page(); }, false) //取消任务 document.querySelectorAll("button")[1].addEventListener("click", function() { observer.unsubscribe("compelte") alert("注销了订阅了compelte事件") }, false) </script> </body> </html>
/** * 事件 * 观察者模式 */ var Observer = (function(slice) { function bind(event, fn) { var events = this.events = this.events || {}, parts = event.split(/\s+/), i = 0, num = parts.length, part; if (events[event] && events[event].length) return this; for (; i < num; i++) { events[(part = parts[i])] = events[part] || []; events[part].push(fn); } return this; } function one(event, fn) { this.bind(event, function fnc() { fn.apply(this, slice.call(arguments)); this.unbind(event, fnc); }); return this; } function unbind(event, fn) { var events = this.events, eventName, i, parts, num; if (!events) return; parts = event.split(/\s+/); for (i = 0, num = parts.length; i < num; i++) { if ((eventName = parts[i]) in events !== false) { events[eventName].splice(events[eventName].indexOf(fn), 1); if (!events[eventName].length) { //修正没有事件直接删除空数组 delete events[eventName]; } } } return this; } function trigger(event) { var events = this.events, i, args, falg; if (!events || event in events === false) return; args = slice.call(arguments, 1); for (i = events[event].length - 1; i >= 0; i--) { falg = events[event][i].apply(this, args); } return falg; //修正带返回 } return function() { this.on = this.subscribe = bind; this.off = this.unsubscribe = unbind; this.trigger = this.publish = trigger; this.one = one; return this; }; })([].slice);