前言
準(zhǔn)備研究一下MVVM的一些東西聘殖,由于MVVM運(yùn)用了觀察者模式的思想,因此翻開(kāi)了《JavaScript設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐》一書晕翠,將觀察者模式學(xué)習(xí)了一遍朦肘,順便有對(duì)一些常用的設(shè)計(jì)模式進(jìn)行一些了解,但還是有很多不能理解的地方嘱蛋,還需努力啊蚯姆。
一、什么是觀察者模式
觀察者模式又叫做發(fā)布—訂閱模式洒敏,是我們最常用的設(shè)計(jì)模式之一龄恋。它定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)凶伙,所有依賴于它的對(duì)象都將得到通知和更新郭毕。觀察者模式提供了一個(gè)訂閱模型,其中對(duì)象訂閱事件并在發(fā)生時(shí)得到通知函荣,這種模式是事件驅(qū)動(dòng)的編程基石显押,它有利益于良好的面向?qū)ο蟮脑O(shè)計(jì)。
看了上面的這段描述傻挂,可能還是不懂什么是觀察者模式乘碑。我們還可以來(lái)看看生活中的觀察者模式:現(xiàn)在房?jī)r(jià)這么高,你肯定是想要早點(diǎn)買房金拒,但你看好的樓盤還沒(méi)開(kāi)盤兽肤,因此你就將你的電話留給售樓小姐,一旦樓盤推出就讓她打電話給你绪抛。主動(dòng)權(quán)在售樓方资铡,而你只需要提供一個(gè)聯(lián)系方式就行了這樣你就不用擔(dān)心樓盤出來(lái)你不知道了,也不需要每天都打電話去問(wèn)樓盤推出了沒(méi)幢码。
二笤休、觀察者模式的使用場(chǎng)景
2.1 DOM事件
實(shí)際上,只要我們?cè)?jīng)在DOM節(jié)點(diǎn)上面綁定過(guò)事件函數(shù)蛤育,那我們就使用過(guò)觀察者模式宛官,應(yīng)為JS和DOM之間就是實(shí)現(xiàn)了一種觀察者模式葫松。
document.body.addEventListener("click", function() {
alert("Hello World")
},false )
document.body.click() //模擬用戶點(diǎn)擊
在上面的代碼中底洗,需要監(jiān)聽(tīng)用戶點(diǎn)擊 document.body 的動(dòng)作腋么,但是我們是沒(méi)辦法預(yù)知用戶將在什么時(shí)候點(diǎn)擊的。因此我們訂閱了 document.body 的 click 事件亥揖,當(dāng) body 節(jié)點(diǎn)被點(diǎn)擊時(shí)珊擂,body 節(jié)點(diǎn)便會(huì)向訂閱者發(fā)布 "Hello World" 消息。
2.2自定義事件
除了DOM事件外费变,我們還可以實(shí)現(xiàn)一些自定義事件摧扇,這種依靠自定義時(shí)間完成的觀察者模式可以用于任何的JavaScript代碼中。
示例:
const event = {
clientList: [],
listen: function(key , fn) {
if (this.clientListen[key]) {
this.clientList[key] = []
}
this.clientList[key].push(fn)
},
trigger: function() {
const key = Array.prototype.shift.call(arguments)
const fns = this.clientList[key]
if (!fns || fns.length === 0 ) {
return false
}
for (let i = 0, fn ;fn = fns[i++];) {
fn.apply(this, arguments)
}
},
remove : function(key , fn) {
const fns = this.clientList[key]
if (!fns) {
return false
}
if (!fn) {
fns && (fns.length = 0)
} else {
for (let l = fns.length - 1; l>=0; l--) {
const _fn = fns[l]
if ( _fn ===fn) {
fns.splice(l, 1)
}
}
}
}
const installEvent = (obj) => {
for (let i in event) {
obj[i] = event[i]
}
}
然后就能增加發(fā)布和訂閱功能了:
const events = {}
installEvent(events)
// 訂閱信息
events.listen('newMessage',fn1 = (say) => {
console.log('say:' + say)
})
// 發(fā)布信息
events.trigger('newMessage',"Hello world")
//移除訂閱
events.remove('newMessage',fn1)
三挚歧、觀察者模式的不足
觀察者模式的有點(diǎn)非常明顯:一是時(shí)間上的解耦扛稽,而是對(duì)象之間的解耦。既可用于異步編程中滑负,也可以用幫助我們完成更松耦合的代碼編寫在张。但它仍然有所不足:
- 創(chuàng)建訂閱者本身要消耗一定的時(shí)間和內(nèi)存
- 當(dāng)訂閱一個(gè)消息時(shí),也許此消息并沒(méi)有發(fā)生矮慕,但這個(gè)訂閱者會(huì)始終存在內(nèi)存中帮匾。
- 觀察者模式弱化了對(duì)象之間的聯(lián)系,這本是好事情痴鳄,但如果過(guò)度使用瘟斜,對(duì)象與對(duì)象之間的聯(lián)系也會(huì)被隱藏的很深,會(huì)導(dǎo)致項(xiàng)目的難以跟蹤維護(hù)和理解痪寻。