自定義事件機制——觀察者模式
觀察者模式是對應用系統進行抽象的有力手段。你可以定義一些事件供其他開發人員使用,而并不需要為此深入了解他們的代碼。一個事件可以被多個訂閱者訂閱,而一個訂閱者也可以訂閱多個不同的事件。對于瀏覽器這類互動環境來說這非常理想。現在的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>

浙公網安備 33010602011771號