观察者模式是对应用系统进行抽象的有力手段。你可以定义一些事件供其他开发人员使用,而并不需要为此深入了解他们的代码。一个事件可以被多个订阅者订阅,而一个订阅者也可以订阅多个不同的事件。对于浏览器这类互动环境来说这非常理想。现在的Web应用程序越来越大,在此背景下,作为一种提高代码的可维护性和简洁性的有力手段,可观察对象的作用更显突出。这种模式的应用有助于防止第三方开发人员和合作伙伴因为对你的应用程序的细节了解得太多而把事情搞糟。提高程的高度解耦有助于程序代码的维护工作。
下面是基于javascript的观察者模式:
var Events = (function (W) { var slice = Array.prototype.slice hasOwen = Object.prototype.hasOwnProperty; function emperty(o) { for (var k in o) { return false; } return true; } if (!String.prototype.trim){ String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/) } } var Events = function () { }; // 发布方法 Events.prototype.fire = function (types) { var len = arguments.length, events=this._events||(this._events={}), _fn, args = []; if (emperty(events)) { return this; } // 无参数触发全部监听 if (len == 0) { types = events; } else if (typeof types == 'string') { types = types.split(/\s+/); args = slice.call(arguments, 1); } for (var type in types) { if (typeof(types.length)=='number'){ type = types[type] } fns = (events[type] || []).slice(); if (fns.length == 0) { continue; } for (; _fn = fns.shift();) { _fn.apply(null, args); } } return this; }; //订阅方法 Events.prototype.on = function (types, fn) { var type, fns, events = this._events||(this._events={}); if (!types||!fn||typeof types != 'string') { return this; } for (types = types.split(/\s+/); type = types.shift();) { fns = events[type] || (events[type] = []); fns.push(fn) } return this; }; //退订方法 Events.prototype.off = function (types, fn) { var type, fns, events = this._events, len = arguments.length, flen; if (len == 0) { this._events = {}; return this; } for (types = types.split(/\s+/); type = types.shift();) { if (!events[type]) { continue; } if (len == 1) { delete events[type]; continue; } fns = events[type]; flen = fns.length; for (; flen--;) { if (fns[flen] == fn) { fns.splice(flen, 1); } } } return this; }; Events.create = function(o){ var to = o.prototype || o; var from = Events.prototype; for (var key in from){ if (hasOwen.call(from,key)){ to[key] = from[key]; } } return o; } return Events; }(window));
有两种用法:
第一种用法,创建Events对象实例:
// 创建事件监听器 var ev = new Events(); var fn1 = function(){ console.log('the first example!'); } var fn2 = function(){ console.log('the two example!'); } // 注册事件 ev.on('example',fn1); // 同时注册多个监听 ev.on('example2 example3 example4',fn2); // 当然也可以同时监听同一件事件 ev.on('example5',fn1); // 注册匿名函数 ev.on('example_1',function(){ console.log('the first example_1') }) // 触发监听 ev.fire();
当然也可以移除事件监听:// 移除所有监听
ev.off(); // 移除多个监听 //ev.off('example example2'); // 移除所有单个监听 //ev.off('example3');
// 移除某个监听的指定事件
// ev.off('example5',fn1)
当然也可以同时触发多个事件监听:
// 触发有监听 ev.fire('example'); // 触发所有监听 ev.fire(); // 触发多个监听 ev.fire('example2 example3 example4');
第二种用法,继承Events事件,Events的create方法:
var Paper = Events.create(function(name, pages, price) { this.name = name; this.pages = pages; this.price = price; });
上面创建了一个Paper类,同时继承了Events事件机制。
当然也可以先创建Paper类,然后再继承Events事件机制
var Paper = function(name, pages, price) { this.name = name; this.pages = pages; this.price = price; }; Events.create(Paper); // 还可以 Events.create(Paper.prototype);
下面是一个综合性的实例:
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"/> </head> <body> <button id="sendWeekly">发送周报</button> <button id="sendDaily">发送日报</button> <script> var Events = (function (W) { var slice = Array.prototype.slice hasOwen = Object.prototype.hasOwnProperty; function emperty(o) { for (var k in o) { return false; } return true; } if (!String.prototype.trim){ String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/) } } var Events = function () { }; // 发布方法 Events.prototype.fire = function (types) { var len = arguments.length, events=this._events||(this._events={}), _fn, args = []; if (emperty(events)) { return this; } // 无参数触发全部监听 if (len == 0) { types = events; } else if (typeof types == 'string') { types = types.split(/\s+/); args = slice.call(arguments, 1); } for (var type in types) { if (typeof(types.length)=='number'){ type = types[type] } fns = (events[type] || []).slice(); if (fns.length == 0) { continue; } for (; _fn = fns.shift();) { _fn.apply(null, args); } } return this; }; //订阅方法 Events.prototype.on = function (types, fn) { var type, fns, events = this._events||(this._events={}); if (!types||!fn||typeof types != 'string') { return this; } for (types = types.split(/\s+/); type = types.shift();) { fns = events[type] || (events[type] = []); fns.push(fn) } return this; }; //退订方法 Events.prototype.off = function (types, fn) { var type, fns, events = this._events, len = arguments.length, flen; if (len == 0) { this._events = {}; return this; } for (types = types.split(/\s+/); type = types.shift();) { if (!events[type]) { continue; } if (len == 1) { delete events[type]; continue; } fns = events[type]; flen = fns.length; for (; flen--;) { if (fns[flen] == fn) { fns.splice(flen, 1); } } } return this; }; Events.create = function(o){ var to = o.prototype || o; var from = Events.prototype; for (var key in from){ if (hasOwen.call(from,key)){ to[key] = from[key]; } } return o; } return Events; }(window)); var Paper = Events.create(function(name, pages, price) { this.name = name; this.pages = pages; this.price = price; }); Paper.prototype.send = function (types) { this.fire(types, { name:this.name, pages:this.pages, price:this.price }); }; function Person(name) { var that = this; this.name = name; this.recive = function (paper) { console.log(that.name + ' recive Pager detail:\n' + 'name:' + paper.name + '\npages:' + paper.pages + '\nprice:' + paper.price) } } var person = new Person('Lucy'), person1 = new Person('Tom'); var Weekly = new Paper('weekly', 298, '$6'), Daily = new Paper('daily', 7, '$0.8'); Weekly.on('weekly', person.recive), Weekly.on('weekly', person1.recive); Daily.on('daily', person.recive); Weekly.off('weekly',person1.recive) var $ = function (id) { return document.getElementById(id); } $('sendWeekly').onclick = function () { Weekly.send('weekly'); } $('sendDaily').onclick = function () { Daily.send('daily'); } </script> </body> </html>