基本概念介紹
觀察者(observer) 模式廣泛用於客戶端Javascript編程中。所有的瀏覽器事件都是該模式的例子。它的另一個名字也稱為自定義事件(custom events),與那些由瀏覽器觸發的事件相比,自定義事件表示是由你編程實現的事件。此外,該模式的另一個別名也稱為訂閱/發布(subscriber/publisher)模式。
設計該模式背后的主要動力是促進形成松散耦合。在這種模式中,並不是一個對象調用另一個對象的方法,而是一個對象訂閱另一個對象的特定活動並在狀態改變后獲得通知。訂閱者也稱為觀察者,而補觀察的對象稱為發布者或主題。當發生了一個重要的事件時,發布者將會通知(調用)所有訂閱者並且可能經常以事件對象的形式傳遞消息。
示例:雜志訂閱
假設有一個發布者paper,它每天出版報紙及月刊雜志。訂閱者joe將被通知任何時候所發生的新聞。
該paper對象需要一個subscribers(topics )屬性,該屬性是一個存儲所有訂閱者的數組。訂閱行為只是將其加入到這個數組中。當一個事件發生時,paper將會循環遍歷訂閱者列表並通知它們。通知意味着調用訂閱者對象的某個方法。故當用戶訂閱信息時,該訂閱者需要向paper的subscribe()提供它的其中一個方法。
paper也提供了unsubscribe()方法,該方法表示從訂閱者數組(即subscribers屬性)中刪除訂閱者。paper最后一個重要的方法是publish(),它會調用這些訂閱者的方法,總而言之,發布者對象paper需要具有以下這些成員:
1.subscribers 一個數組 2.subscribe() 將訂閱者添加到subscribers數組中 3.unsubscribe() 從subscribers數組中刪除訂閱者 4.publish() 循環遍歷subscribers數組中的每一個元素,並且調用他們注冊時所提供的方法
所有這三種方法都需要一個topic參數,因為發布者可能觸發多個事件(比如同時發布一本雜志和一份報紙)而用戶可能僅選擇訂閱其中一種,而不是另外一種。
由於這些成員對於任何發布者對象都是通用的,故將它們作為獨立對象的一個部分來實現是很有意義的。那樣我們可將其復制到任何對象中,並將任意給定對象變成一個發布者。
JS里對觀察者模式的實現是通過回調來實現的,我們來先定義一個pubsub對象,其內部包含了3個方法:訂閱、退訂、發布。
var pubsub = {}; (function (q) { var topics = {}, // 回調函數存放的數組 subUid = -1; // 發布方法 q.publish = function (topic, args) { if (!topics[topic]) { return false; } setTimeout(function () { var subscribers = topics[topic], len = subscribers ? subscribers.length : 0; while (len--) { subscribers[len].func(topic, args); } }, 0); return true; }; //訂閱方法 q.subscribe = function (topic, func) { if (!topics[topic]) { topics[topic] = []; } var token = (++subUid).toString(); topics[topic].push({ token: token, func: func }); return token; }; //退訂方法 q.unsubscribe = function (token) { for (var m in topics) { if (topics[m]) { for (var i = 0, j = topics[m].length; i < j; i++) { if (topics[m][i].token === token) { topics[m].splice(i, 1); return token; } } } } return false; }; } (pubsub));
使用方式如下:
//來,訂閱一個 pubsub.subscribe('example1', function (topics, data) { console.log(topics + ": " + data); }); //發布通知 pubsub.publish('example1', 'hello world!'); pubsub.publish('example1', ['test', 'a', 'b', 'c']); pubsub.publish('example1', [{ 'color': 'blue' }, { 'text': 'hello'}]);
試試多個訂閱者訂閱同個主題:
//來,訂閱一個 pubsub.subscribe('example1', function (topics, data) { console.log(topics + ": " + data); }); //來,再訂閱一個 pubsub.subscribe('example1', function (topics, data) { console.log(topics + "******* " + data); }); //發布通知 pubsub.publish('example1', 'hello world!');
輸出:
example1***** hello world!
example1: hello world!
參考:http://www.2cto.com/kf/201210/163500.html
http://www.cnblogs.com/TomXu/archive/2012/03/02/2355128.html