觀察者模式定義了對象之間一對對多的依賴關係,當一個對象改變了狀態,它的全部依賴會被通知,而後自動更新。javascript
和其餘模式相比,這種模式又增長了一個原則:html
因此主要的想法是咱們要有一個大的對象來處理訂閱(Subject/Observable),以及不少對象(Observers)被訂閱而後等待事件觸發。java
還有一個重要的點就是Observers接受消息的順序是隨機的,因此你不要依照Observers添加的順序。jquery
var Observable = { observers: [] , addObserver: function(observer) { this.observers.push(observer) } , removeObserver: function(observer) { var index = this.observers.indexOf(observer) if (~index) { this.observers.splice(index, 1) } } , notifyObservers: function(message) { for (var i = this.observers.length - 1; i >= 0; i--) { this.observers[i](message) }; } } Observable.addObserver(function(message){ console.log("First observer message:" + message) }) var observer = function(message){ console.log("Second observer message:" + message) } Observable.addObserver(observer) Observable.notifyObservers('test 1') // Second observer message:test 1 // First observer message:test 1 Observable.removeObserver(observer) Observable.notifyObservers('test 2') // First observer message:test 2複製代碼
若是你想用某種形式的ID來刪除,而不是傳入回調函數,那麼代碼須要改爲下面這樣:git
var Observable = { observers: [] , lastId: -1 , addObserver: function(observer) { this.observers.push({ callback: observer , id: ++this.lastId }) return this.lastId } , removeObserver: function(id) { for (var i = this.observers.length - 1; i >= 0; i--) { this.observers[i] if (this.observers[i].id == id) { this.observers.splice(i, 1) return true } } return false } , notifyObservers: function(message) { for (var i = this.observers.length - 1; i >= 0; i--) { this.observers[i].callback(message) }; } } var id_1 = Observable.addObserver(function(message){ console.log("First observer message:" + message) }) var observer = function(message){ console.log("Second observer message:" + message) } var id_2 = Observable.addObserver(observer) Observable.notifyObservers('test 1') Observable.removeObserver(id_2) Observable.notifyObservers('test 2')複製代碼
觀察者模式有兩種重要的策略:github
Push - 當一個事件發生後將通知全部觀察者,並把全部的新數據傳給他們api
Pull - 當一個事件發生後將通知全部觀察者,每一個觀察者將拉取本身須要的數據數組
當你只想要你須要的數據,Pull 方法更爲可取,在下面這個例子中,Subject將會通知觀察者發生了改變,而後每一個觀察者取它們本身須要的數據。而且在這個例子中,咱們隱藏觀察者數組,而且將各自的私有數據存到閉包中。bash
var Observable = {} ;(function(O){ var observers = [] , privateVar O.addObserver = function(observer) { observers.push(observer) } O.removeObserver = function(observer) { var index = observers.indexOf(observer) if (~index) { observers.splice(index, 1) } } O.notifyObservers = function() { for (var i = observers.length - 1; i >= 0; i--) { observers[i].update() }; } O.updatePrivate = function(newValue) { privateVar = newValue this.notifyObservers() } O.getPrivate = function() { return privateVar } }(Observable)) Observable.addObserver({ update: function(){ this.process() } , process: function(){ var value = Observable.getPrivate() console.log("Private value is: " + value) } }) Observable.updatePrivate('test 1') // Private value is: test 1 Observable.updatePrivate('test 2') // Private value is: test 2複製代碼
爲了避免建立多個可觀察對象,最好在觀察者模式中加入主題功能,最簡單的形式看起來是這樣的:markdown
var Observable = { observers: [] , addObserver: function(topic, observer) { this.observers[topic] || (this.observers[topic] = []) this.observers[topic].push(observer) } , removeObserver: function(topic, observer) { if (!this.observers[topic]) return; var index = this.observers[topic].indexOf(observer) if (~index) { this.observers[topic].splice(index, 1) } } , notifyObservers: function(topic, message) { if (!this.observers[topic]) return; for (var i = this.observers[topic].length - 1; i >= 0; i--) { this.observers[topic][i](message) }; } } Observable.addObserver('cart', function(message){ console.log("First observer message:" + message) }) Observable.addObserver('notificatons', function(message){ console.log("Second observer message:" + message) }) Observable.notifyObservers('cart', 'test 1') // First observer message:test 1 Observable.notifyObservers('notificatons', 'test 2') // Second observer message:test 2複製代碼
更高級的功能會有以下特性:
子話題 (好比 /bar/green 或者 bar.green)
發佈到主題傳播到子主題
發佈到全部主題
訂閱者優先級
jQuery有一個很好的特性,$.Callback。除了經典的觀察功能外,他還有其餘的一組標記:
once: 確保callback列表只能被觸發一次
memory: 跟蹤數據
unique: 確保回調函數只會被添加一次。
stopOnFalse: 當回調函數返回false,則中斷調用。
使用這些選項你能夠定製的你的觀察者。讓咱們來看看最基礎的例子:
var callbacks = jQuery.Callbacks() , Topic = { publish: callbacks.fire, subscribe: callbacks.add, unsubscribe: callbacks.remove } function fn1( value ){ console.log( "fn1: " + value ); } function fn2( value ){ console.log("fn2: " + value); } Topic.subscribe(fn1); Topic.subscribe(fn2); Topic.publish('hello world!'); Topic.publish('woo! mail!');複製代碼
若是你想看一些複雜的例子,請點這裏。
下面是一個簡單的例子,幾乎相同的例子能夠在「CoffeeScript Cookbook [7]」中找到。
class Observable constructor: () -> @subscribers = [] subscribe: (callback) -> @subscribers.push callback unsubscribe: (callback) -> @subscribers = @subscribers.filter (item) -> item isnt callback notify: () -> subscriber() for subscriber in @subscribers class Observer1 onUpdate: () -> console.log "1st got new message" class Observer2 onUpdate: () -> console.log "2nd updated" observable = new Observable() observer1 = new Observer1() observer2 = new Observer2() observable.subscribe observer1.onUpdate observable.subscribe observer2.onUpdate observable.notify()複製代碼
(github) shichuan / javascript-patterns / design-patterns / observer.html and jQuery examples
(書) JavaScript Patterns: Build Better Applications with Coding and Design Patterns
(書) Learning JavaScript Design Patterns: A JavaScript and jQuery Developer's Guide
(書) Pro JavaScript Design Patterns: The Essentials of Object-Oriented JavaScript Programming
原文:bumbu.github.io/javascript-…譯者:繆宇