以前在咱們項目中有一些方法常用,可是我本覺得那些方法是老大封裝的mvvm框架中原本就存在的監聽方法,後來時間富裕的時候,我看了看不少項目的老代碼,原來我以前一直使用的this.watch方法和this.publish方法是每一個實例對象的基類中定義的方法。而後我找到了該基類,原來基類也是繼承了一個叫作PubSub的類,以後我看了PubSub這個不足100行代碼的類,它只有三個方法subscribe,unsubscribe,publish哦!原來這就是我上學時期學習過的發佈-訂閱設計模式,可是那時候只是看了看書,知道和了解了當時書上的案例和講解,然而從沒有把它手動應用到過項目中,因此就致使了本身用的方法都不知其原理和不知其然,趕忙好好反覆的跟了一下項目的代碼和方法的執行,又到網上搜了相關的文檔,如今纔算是知其然吧,突然發現寫業務寫久了就容易不去思考,不去追求爲何,也以此來警示一下本身吧,對待任何事情都應該用學者的心態去對待。javascript
公衆號你們都關注過吧,你訂閱了某個平臺或者某人的公衆號,當他推送消息後,你就能夠閱讀他的新文章,並且誰均可以關注這個公衆號主體。這一過程其實就是一個發佈-訂閱設計模式的現實場景,它是一種一對多的形式,公衆號的主體就是咱們的發佈者,全部的讀者就是訂閱者,並且咱們隨時能夠取消關注這個公衆號,因此也會提供相應的取消訂閱方法。java
做爲發佈者應該提供什麼方法給訂閱者(提供subscribe)程序員
發佈者提供了訂閱的方法後應該將這些訂閱者都存起來,記錄以便往後給他們推送消息(list用來存儲)設計模式
做爲發佈者要有推送消息的方法(publish方法)app
訂閱者如何訂閱消息或者事件,訂閱者還能夠取消訂閱框架
代碼實現mvvm
//定義一個發佈者對象
var pubsubObj = {};
pubsubObj.list = {}; // 此處修改一下由於會有不一樣類型的訂閱 用於存儲訂閱者的回調函數
//給訂閱者提供訂閱的方法
pubsubObj.subscribe = function (key,callbak) {
if (!this.list[key]) {
//若是沒有訂閱此事件,給此事件建立一個列表
this.list[key] = [];
}
this.list[key].push(callbak);
}
//發佈消息的功能
pubsubObj.publish = function (key,args) {
var fns = this.list[key]; // 取出該消息對應的回調函數的集合
// 若是沒有訂閱過該消息的話,則返回
if(!fns || fns.length === 0) {
return;
}
var len = fns.length;
//執行對應的訂閱函數集合
while (len--) {
fns[len].apply(this,args);
}
}
//取消訂閱的功能
pubsubObj.unsubscribe = function(key,fn){
var fns = this.list[key];
if(!fns) {
return false;
}
//若是沒有傳回調則證實取消key事件的全部訂閱
if (!fn) {
fns && (fns.length = 0);
}
else {
//將和參數fn相同的回調刪除
var len = fns.length;
while(len--){
if(fns[len] === fn){
fns.splice(len, 1);
}
}
}
};
複製代碼
var pubsubClass = {
list: {},
subscribe:function (key,callbak) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(callbak);
},
publish: function (key,args) {
var fns = this.list[key];
if(!fns || fns.length === 0) {
return;
}
var len = fns.length;
while (len--) {
fns[len].apply(this,args);
}
},
unsubscribe:function(key,fn){
var fns = this.list[key];
if(!fns) {
return false;
}
if (!fn) {
fns && (fns.length = 0);
}
else {
//將和參數fn相同的回調刪除
var len = fns.length;
while(len--){
if(fns[len] === fn){
fns.splice(len, 1);
}
}
}
}
};
複製代碼
看完了以上的代碼,咱們會發現只定義一個pubsubObj對象,這些功能只能用在這一個對象上,而不能複用,因此封裝好以後將它封裝成一個對象,可讓其餘對象來使用它的功能,也能夠根據大家項目的框架將它封裝爲一個類等作法來進行不一樣的實現函數
在咱們的項目中,定義了一個基類PubSub類,在須要用到發佈訂閱模式的功能時,便可給當前的對象繼承該類的功能來使用PubSub類的方法。學習
實際場景:電商平臺當用戶購買一件商品時,購物車就會收到相關的產品信息,購物車組件的參數和屬性須要及時變動,這個場景就很是適合使用PubSub類優化
當用戶在頁面中購買一本書的時候,會給購物車作狀態等一些變動
//購買一件商品
addBook:function(book){
//變動一下購物車的狀態信息等
this.refreshState();
return this.saveAddBook(book,function() {
//調用接口把書加入購物車邏輯
}.bind(this));
return true;
},
refreshState: function(){
this.selectedBookCount = this.getSelectedBookCount();
this.totalCount = this.getTotalCount();
this.publish('change', [this]); //執行通知
},
//將訂閱方法在這個基類中定義爲watch監聽方法
watch: function(eventName, callback) {
return this.subscribe(''+eventName, callback);
},
複製代碼
購物車對象
init:function(){
this.shopcart.watch('change', this.cartChanged.bind(this)),
},
cartChanged: function(cart) {
//當前相關邏輯操做
},
複製代碼
經過文字的描述還有實際代碼的講解,應該對javascript中發佈-訂閱設計模式有了基本的瞭解和學習。最後的代碼示例和實際場景是我本身編造的demo,可是很是適合真實的開發環境中,學是一個獲取新知的過程,習則是將學習的內容內化的過程,之後在寫代碼和寫業務邏輯的時候要給本身創造學習的機會,而不只僅是代碼能跑,邏輯能通就能夠。
Cayley 一個不斷努力學習的女程序員