一个被观察者的对象,经过注册的方式维护一组观察者对象。当被观察者发生变化,就会产生一个通知,经过广播的方式发送出去,最后调用每一个观察者的更新方法。当观察者再也不须要接受被观察者的通知时,被观察者能够将该观察者从所维护的组中删除。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!')