// 定義發佈者 var salesOffices = {}; // 緩存列表, 存放訂閱者的回調函數 salesOffices.clientList = []; // 定義訂閱者 salesOffices.listen = function (fn) { this.clientList.push(fn); } // 發佈消息 salesOffices.trigger = function () { for (var i = 0, fn; fn = this.clientList[i++];) { fn.apply(this, arguments) } } /*** 測試 ***/ // 訂閱者1 salesOffices.listen(function (price, squareMeter) { console.log('價格=' + price); console.log('squareMeter= ' + squareMeter); }); // 訂閱者2 salesOffices.listen(function (price, squareMeter) { console.log('價格=' + price); console.log('squareMeter= ' + squareMeter); }); // 發佈消息 salesOffices.trigger(2000, 80); salesOffices.trigger(3000, 100);
// 定義發佈者 var salesOffices = {}; // 緩存列表, 存放訂閱者的回調函數 salesOffices.clientList = {}; // 定義訂閱者 (增長緩存, 增長標示 key ) salesOffices.listen = function (key, fn) { if (!this.clientList[key]) { this.clientList[key] = []; } this.clientList[key].push(fn); } // 發佈消息 salesOffices.trigger = function () { var key = Array.prototype.shift.call(arguments), fns = this.clientList[key]; if (!fns || fns.length === 0) { return false; } for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments) } }
// 核心事件 var event = { clientList: {}, listen: function (key, fn) { if (!this.clientList[key]) { this.clientList[key] = []; } this.clientList[key].push(fn); }, trigger: function () { var key = Array.prototype.shift.call(arguments), fns = this.clientList[key]; if (!fns || fns.length === 0) { return false } for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments); } } } // 取消訂閱消息 event.remove = function (key, fn) { var fns = this.clientList[key]; if (!fns) { return false; } if (!fn) { // 沒有傳入fn 取消key對應的全部的訂閱 fns && (fns.length = 0); } else { // 傳入fn 刪除對應的fn for (var l = fns.length - 1; l >= 0; l--) { var _fn = fns[l]; if (_fn === fn) { fns.splice(l, 1) } } } } // 給任何對象動態增長髮布-訂閱功能 var installEvent = function (obj) { for (var key in event) { if (event.hasOwnProperty(key)) { obj[key] = event[key]; } } } /*** 測試 ***/ var salesOffices = {}; installEvent(salesOffices); // 訂閱者1 salesOffices.listen('squareMeter80', function (price) { console.log('價格=' + price); }); // 訂閱者2 salesOffices.listen('squareMeter100', function (price) { console.log('價格=' + price); }); // 發佈消息 salesOffices.trigger('squareMeter80', 20000); salesOffices.trigger('squareMeter100', 30000);
// 登陸成功, 發佈成功消息 $.ajax('http://xxx.com/login', function (data) { login.trigger('loginSuccess', data); }); // 這種寫法也很好 var header = (function () { // 訂閱消息 login.listen('loginSuccess', function (data) { header.setAvatar(data); }) return { setAvatar: function (data) { console.log('header 模塊拿到用戶信息') } } })();
以上寫法會有三個問題javascript
var Event = (function() { var global = this, Event, _default = 'default'; Event = function () { var _listen, _trigger, _remove, _slice = Array.prototype.slice, _shift = Array.prototype.shift, _unshift = Array.prototype.unshift, namespaceCache = {}, _create, find, each = function(arr, fn) { var ret; for (var i = 0, l = arr.length; i < l; i++) { var n = arr[i]; ret = fn.call(n, i, n); } return ret; }; _listen = function (key, fn, cache) { if (!cache[key]) { cache[key] = []; } cache[key].push(fn); }; _remove = function (key, cache, fn) { if (cache[eky]) { if (fn) { for (var i = cache[key].length - 1; i >= 0; i--) { if (cache[key][i] === fn) { cache[key].splice(i, 1); } } } else { cache[key] = []; } } } _trigger = function () { var cache = _shift.call(arguments), key = _shift.call(arguments), args = arguments, _self = this, ret, stack = cache[key]; if (!stack || !stack.length) { return; } return each(stack, function () { return this.apply(_self, args); }) }; _create = function (namespace) { namespace = namespace || _default; var cache = {}, offlineStack = [], ret = { listen: function (key, fn, last) { _listen(key, fn, cache); if (offlineStack === null) { return; } if (last === 'last') { offlineStack.length && offlineStack.pop()(); } else { each(offlineStack, function () { this(); }) } offlineStack = null; }, one: function (key, fn, last) { _remove(key, fn, cache); this.listen(key, fn, last); }, remove: function (key, fn) { _remove(key, cache, fn); }, trigger: function () { var fn, args, _self = this; _unshift.call(arguments, cache); args = arguments; fn = function () { return _trigger.apply(_self, args); } if (offlineStack) { return offlineStack.push(fn); } return fn(); } }; return namespace ? (namespaceCache[namespace] ? namespaceCache[namespace] : namespaceCache[namespace] = ret) : ret } return { create: _create, one: function (key, fn, last) { var event = this.create(); event.one(key, fn, last); }, remove: function (key, fn) { var event = this.create(); event.remove(key, fn); }, listen: function (key, fn, last) { var event = this.create(); event.listen(key, fn, last); }, trigger: function () { var event = this.create(); event.trigger.apply(this, arguments); } } }() return Event; })();
// subject(發佈) 中的目標發生變化. Observer(觀察) 能接受到變化 function ObserverList() { this.observerList = []; } ObserverList.prototype.add = function (obj) { return this.observerList.push(obj); } ObserverList.prototype.get = function (index) { if (index > -1 && this.observerList.length) { return this.observerList[index]; } } ObserverList.prototype.indexOf = function (obj, startIndex) { var i = startIndex; while(i < this.observerList.length) { if (this.observerList[i] === obj) { return i; } i++; } return -1; } ObserverList.prototype.removeAt = function (index) { this.observerList.splice(index, 1); } // 發佈者 function Subject() { this.observers = new ObserverList(); } Subject.prototype.addObserver = function (observer) { this.observers.add(observer); } Subject.prototype.removeObserver = function (observer) { this.observers.removeAt(this.observers.indexOf(observer, 0)); } Subject.prototype.notify = function (context) { var observerCount = this.observers.count(); for (var i = 0; i < observerCount; i++) { this.observers.get(i).update(context); } } // 觀察者 function Observer() { this.update = function () { // ... } }
var pubsub = {}; (function (myObject) { var topics = {}; var subUid = -1; // 發佈 myObject.publish = function (topic, args) { if (!topics[topic]) { return false; } var subscribers = topics[topic], len = subscribers ? subscribers.length : 0; while (len--) { subscribers[len].func(topic, args); } return this; } // 訂閱者 myObject.subscribe = function (topic, func) { if (!topics[topic]) { topics[topic] = []; } var token = (++subUid).toString(); topics[topic].push({ token: token, func: func }) } // 取消訂閱 myObject.unsubscribe = function (token) { for (var m in topics) { if (topics[m]) { for (var i = 0, j = topics[m].length; i < j; i++) { if (topics[m][i].token === token) { topics[m].splice(i, 1); return token; } } } } return this; } })(pubsub)