一個被觀察者的對象,經過註冊的方式維護一組觀察者對象。當被觀察者發生變化,就會產生一個通知,經過廣播的方式發送出去,最後調用每一個觀察者的更新方法。當觀察者再也不須要接受被觀察者的通知時,被觀察者能夠將該觀察者從所維護的組中刪除。html
這個實現包含如下組件:ajax
首先,對被觀察者維護的一組觀察者(列表)進行建模編程
function ObserverList() { this.observerList = [] } ObserverList.prototype.add = function(obj) { return this.observerList.push(obj) } ObserverList.prototype.Empty = function() { this.observerList = [] } ObserverList.prototype.removeAt = function(index) { this.observerList.splice(index, 1) } ObserverList.prototype.count = function() { return this.observerList.length } ObserverList.prototype.get = function(index) { if (index > -1 && index < this.observerList.length) { return this.observerList[index] } } // Extend an object with an extension function extend(extension, obj) { for (var key in extension) { obj[key] = extension[key] } }
接着,對被觀察者以及其增長、刪除、通知能力進行建模設計模式
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) } }
接着,對觀察者進行建模,這裏的 update 函數以後會被具體的行爲覆蓋app
function Observer() { this.update = function() { // ... } }
咱們使用上面的觀察者組件,如今咱們定義異步
一個容器,用於放置新的選擇框異步編程
<button id="addNewObserver">Add New Observer checkbox</button> <input id="mainCheckbox" type="checkbox"/> <div id="observersContainer"></div>
// DOM 元素的引用 var controlCheckbox = document.getElementById('mainCheckbox'), addBtn = document.getElementById('addNewObserver'), container = document.getElementById('observersContainer') // 具體的被觀察者 // Subject 類擴展 controlCheckbox extend(new Subject(), controlCheckbox) //點擊 checkbox 將會觸發對觀察者的通知 controlCheckbox['onclick'] = new Function( 'controlCheckbox.notify(controlCheckbox.checked)' ) addBtn['onclick'] = AddNewObserver // 具體的觀察者 function AddNewObserver() { // 創建一個新的用於增長的 checkbox var check = document.createElement('input') check.type = 'checkbox' // 使用 Observer 類擴展 checkbox extend(new Observer(), check) // 使用定製的 update 函數重載 check.update = function(value) { this.checked = value } // 增長新的觀察者到咱們主要的被觀察者的觀察者列表中 controlCheckbox.AddObserver(check) // 將元素添加到容器的最後 container.appendChild(check) }
上述示例中函數
var pubsub = {} ;(function(q) { var topics = {}, subUid = -1 // Publish or broadcast events of interest // with a specific topic name and arguments // such as the data to pass along q.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 } // Subscribe to events of interest // with a specific topic name and a // callback function, to be executed // when the topic/event is observed q.subscribe = function(topic, func) { if (!topics[topic]) { topics[topic] = [] } var token = (++subUid).toString() topics[topic].push({ token: token, func: func }) return token } // Unsubscribe from a specific // topic, based on a tokenized reference // to the subscription q.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)
// Another simple message handler // A simple message logger that logs any topics and data received through our // subscriber var messageLogger = function(topics, data) { console.log('Logging: ' + topics + ': ' + data) } // Subscribers listen for topics they have subscribed to and // invoke a callback function (e.g messageLogger) once a new // notification is broadcast on that topic var subscription = pubsub.subscribe('inbox/newMessage', messageLogger) // Publishers are in charge of publishing topics or notifications of // interest to the application. e.g: pubsub.publish('inbox/newMessage', 'hello world!') // or pubsub.publish('inbox/newMessage', ['test', 'a', 'b', 'c']) // or pubsub.publish('inbox/newMessage', { sender: 'hello@google.com', body: 'Hey again!' }) // We cab also unsubscribe if we no longer wish for our subscribers // to be notified // pubsub.unsubscribe( subscription ); // Once unsubscribed, this for example won't result in our // messageLogger being executed as the subscriber is // no longer listening pubsub.publish('inbox/newMessage', 'Hello! are you still there?')
舊的代碼動畫
$.ajax('http:// xxx.com?login', function(data) { header.setAvatar(data.avatar) // 設置 header 模塊的頭像 nav.setAvatar(data.avatar) // 設置導航模塊的頭像 })
使用了發佈/訂閱模式的代碼this
$.ajax('http:// xxx.com?login', function(data) { pubsub.publish('loginSucc', data) // 發佈登陸成功的消息 }) // header 模塊 var header = (function() { pubsub.subscribe('loginSucc', function(data) { header.setAvatar(data.avatar) }) return { setAvatar: function(data) { console.log('設置 header 模塊的頭像') } } })() // nav 模塊 var nav = (function() { pubsub.subscribe('loginSucc', function(data) { nav.setAvatar(data.avatar) }) return { setAvatar: function(avatar) { console.log('設置 nav 模塊的頭像') } } })()
觀察者模式要求想要接受相關通知的觀察者必須到發起這個事件的被觀察者上註冊這個事件
controlCheckbox.AddObserver(check)
發佈/訂閱模式使用一個主題/事件頻道,這個頻道處於訂閱者和發佈者之間,這個事件系統容許代碼定義應用相關的事件,避免訂閱者和發佈者之間的依賴性
pubsub.subscribe('inbox/newMessage', messageLogger) pubsub.publish('inbox/newMessage', 'hello world!')