關(guān)于Vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定的原理,請(qǐng)點(diǎn)擊:Vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定的原理
原文鏈接:JavaScript設(shè)計(jì)模式之觀察者模式
什么是觀察者模式?
觀察者模式(Observer)通常又被稱為 發(fā)布-訂閱者模式 或 消息機(jī)制,它定義了對(duì)象間的一種一對(duì)多的依賴關(guān)系,只要當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新,解決了主體對(duì)象與觀察者之間功能的耦合,即一個(gè)對(duì)象狀態(tài)改變給其他對(duì)象通知的問(wèn)題。
比如比較當(dāng)下熱門 vue 框架,里面不少地方都涉及到了觀察者模式,比如:

利用
Object.defineProperty()對(duì)數(shù)據(jù)進(jìn)行劫持,設(shè)置一個(gè)監(jiān)聽(tīng)器 Observer,用來(lái)監(jiān)聽(tīng)所有屬性,如果屬性上發(fā)上變化了,就需要告訴訂閱者 Watcher去更新數(shù)據(jù),最后指令解析器 Compile解析對(duì)應(yīng)的指令,進(jìn)而會(huì)執(zhí)行對(duì)應(yīng)的更新函數(shù),從而更新視圖,實(shí)現(xiàn)了雙向綁定~
創(chuàng)建一個(gè)觀察者
首先我們需要?jiǎng)?chuàng)建一個(gè)觀察者對(duì)象,它包含一個(gè)消息容器和三個(gè)方法,分別是訂閱消息方法on , 取消訂閱消息方法 off ,發(fā)送訂閱消息 subscribe 。
const Observe = (function () {
//防止消息隊(duì)列暴露而被篡改,將消息容器設(shè)置為私有變量
let __message = {};
return {
//注冊(cè)消息接口
on : function () {},
//發(fā)布消息接口
subscribe : function () {},
//移除消息接口
off : function () {}
}
})();
注冊(cè)消息方法
注冊(cè)消息方法的作用是將訂閱者注冊(cè)的消息推入到消息隊(duì)列中,因此需要傳遞兩個(gè)參數(shù):消息類型和對(duì)應(yīng)的處理函數(shù),要注意的是,如果推入到消息隊(duì)列是如果此消息不存在,則要?jiǎng)?chuàng)建一個(gè)該消息類型并將該消息放入消息隊(duì)列中,如果此消息已經(jīng)存在則將對(duì)應(yīng)的方法突入到執(zhí)行方法隊(duì)列中。
//注冊(cè)消息接口
on: function (type, fn) {
//如果此消息不存在,創(chuàng)建一個(gè)該消息類型
if( typeof __message[type] === 'undefined' ){
// 將執(zhí)行方法推入該消息對(duì)應(yīng)的執(zhí)行隊(duì)列中
__message[type] = [fn];
}else{
//如果此消息存在,直接將執(zhí)行方法推入該消息對(duì)應(yīng)的執(zhí)行隊(duì)列中
__message[type].push(fn);
}
}
發(fā)布消息方法
發(fā)布消息,其功能就是當(dāng)觀察者發(fā)布一個(gè)消息是將所有訂閱者訂閱的消息依次執(zhí)行,也需要傳兩個(gè)參數(shù),分別是消息類型和對(duì)應(yīng)執(zhí)行函數(shù)時(shí)所需要的參數(shù),其中消息類型是必須的。
//發(fā)布消息接口
subscribe: function (type, args) {
//如果該消息沒(méi)有注冊(cè),直接返回
if ( !__message[type] ) return;
//定義消息信息
let events = {
type: type, //消息類型
args: args || {} //參數(shù)
},
i = 0, // 循環(huán)變量
len = __message[type].length; // 執(zhí)行隊(duì)列長(zhǎng)度
//遍歷執(zhí)行函數(shù)
for ( ; i < len; i++ ) {
//依次執(zhí)行注冊(cè)消息對(duì)應(yīng)的方法
__message[type][i].call(this,events)
}
}
移除消息方法
移除消息方法,其功能就是講訂閱者注銷的消息從消息隊(duì)列中清除,也需要傳遞消息類型和執(zhí)行隊(duì)列中的某一函數(shù)兩個(gè)參數(shù)。這里為了避免刪除是,消息不存在的情況,所以要對(duì)其消息存在性制作校驗(yàn)。
//移除消息接口
off: function (type, fn) {
//如果消息執(zhí)行隊(duì)列存在
if ( __message[type] instanceof Array ) {
// 從最后一條依次遍歷
let i = __message[type].length - 1;
for ( ; i >= 0; i-- ) {
//如果存在改執(zhí)行函數(shù)則移除相應(yīng)的動(dòng)作
__message[type][i] === fn && __message[type].splice(i, 1);
}
}
}
大顯身手
//訂閱消息
Observe.on('say', function (data) {
console.log(data.args.text);
})
Observe.on('success',function () {
console.log('success')
});
//發(fā)布消息
Observe.subscribe('say', { text : 'hello world' } )
Observe.subscribe('success');
我們?cè)谙㈩愋蜑?say的消息中注冊(cè)了兩個(gè)方法,其中有一個(gè)接受參數(shù),另一個(gè)不需要參數(shù),然后通過(guò) subscribe 發(fā)布say 和success消息,結(jié)果跟我們預(yù)期的一樣,控制臺(tái)輸出了hello world以及 success