觀察者模式又叫發佈訂閱模式(Publish/Subscribe),它定義了一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態發生變化時就會通知全部的觀察者對象,使得它們可以自動更新本身。html
使用觀察者模式的好處:前端
以下例子:segmentfault
let publisher = { subscribers: { any: [] }, subscribe: function(fn, type = 'any') { if (typeof this.subscribers[type] === 'undefined') { this.subscribers[type] = [] } this.subscribers[type].push(fn) }, unsubscribe: function(fn, type) { this.visitSubscribers('unsubscribe', fn, type) }, publish: function(publication, type) { this.visitSubscribers('publish', publication, type) }, visitSubscribers: function(action, arg, type = 'any') { this.subscribers[type].forEach((currentValue, index, array) => { if (action === 'publish') { currentValue(arg) } else if (action === 'unsubscribe') { if (currentValue === arg) { this.subscribers[type].splice(index, 1) } } }) } } let funcA = function(cl) { console.log('msg1' + cl) } let funcB = function(cl) { console.log('msg2' + cl) } publisher.subscribe(funcA) publisher.subscribe(funcB) publisher.unsubscribe(funcB) publisher.publish(' in publisher') // msg1 in publisher
這裏能夠經過一個函數 makePublisher() 將一個對象複製成 publisher ,從而將其轉換成一個發佈者。設計模式
function makePublisher(o) { Object.keys(publisher).forEach((curr, index, array) => { if (publisher.hasOwnProperty(curr) && typeof publisher[curr] === 'function') { o[curr] = publisher[curr] } }) o.subscribers={any:[]} } // 發行者對象 let paper = { daily: function() { this.publish('big news today') }, monthly: function() { this.publish('interesting analysis', 'monthly') } } makePublisher(paper) // 訂閱對象 let joe = { drinkCoffee: function(paper) { console.log('Just read daily ' + paper) }, sundayPreNap: function(monthly) { console.log('Reading this monthly ' + monthly) } } paper.subscribe(joe.drinkCoffee) paper.subscribe(joe.sundayPreNap, 'monthly') paper.daily() // Just read daily big news today paper.monthly() // Reading this monthly interesting analysis
使用ES6裏的class稍微改造下:數組
class publisher { constructor() { this.subscribers = { any: [] } } subscribe(fn, type = 'any') { if (typeof this.subscribers[type] === 'undefined') { this.subscribers[type] = [] } this.subscribers[type].push(fn) } unsubscribe(fn, type) { this.visitSubscribers('unsubscribe', fn, type) } publish(publication, type) { this.visitSubscribers('publish', publication, type) } visitSubscribers(action, arg, type = 'any') { this.subscribers[type].forEach((currentValue, index, array) => { if (action === 'publish') { currentValue(arg) } else if (action === 'unsubscribe') { if (currentValue === arg) { this.subscribers[type].splice(index, 1) } } }) } } let publish = new publisher(); let funcA = function(cl) { console.log('msg1' + cl) } let funcB = function(cl) { console.log('msg2' + cl) } publish.subscribe(funcA) publish.subscribe(funcB) publish.unsubscribe(funcB) publish.publish(' in publisher') // msg1 in publisher
以上兩個方法都是《JavaScript模式》裏介紹的,這裏貼上個本身實現的,感受看起來舒服點...緩存
const Observer = (function() { const _message = {} // 消息隊列 return { regist(type, fn) { // 訂閱 _message[type] ? _message[type].push(fn) : _message[type] = [fn] }, emit(type, payload) { // 發佈 if (!_message[type]) { return } _message[type].forEach(event => event(payload)) }, remove(type, fn) { // 退訂 if (!_message[type].includes(fn)) {return} const idx = _message[type].indexOf(fn) _message[type].splice(idx, 1) } } })()
class Observer { constructor() { this._message = {} } regist(type, fn) { // 訂閱 this._message[type] ? this._message[type].push(fn) : this._message[type] = [fn] } emit(type, payload) { // 發佈 if (!this._message[type]) { return } this._message[type].forEach(event => event(payload)) } remove(type, fn) { // 退訂 if (!this._message[type].includes(fn)) {return} const idx = this._message[type].indexOf(fn) this._message[type].splice(idx, 1) } }
觀察者的使用場合就是:當一個對象的改變須要同時改變其它對象,而且它不知道具體有多少對象須要改變的時候,就應該考慮使用觀察者模式。微信
總的來講,觀察者模式所作的工做就是在解耦,讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響到另外一邊的變化。函數
本文是系列文章,能夠相互參考印證,共同進步~學習
網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出~this
參考:
設計模式之觀察者模式
《JavaScript模式》
《Javascript 設計模式》 - 張榮銘
PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~
另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~