js如何實現觀察者模式
阿新 • • 發佈:2019-01-09
定義:觀察者模式,又叫釋出訂閱者模式,又叫訊息系統,又叫訊息機制,又叫自定義事件,解決主體與觀察者之間的耦合問題
特點:
1 解決的是耦合問題(類與類之間,物件之間,類與物件之間,模組之間)
2 對於任何一個觀察者來說,其他觀察者的改變不會影響自身
3 對於任何一個物件來說,既可以是觀察者,也可以是被觀察者
jQuery中的觀察者模式
$.CallBacks()方法執行的結果得到一個觀察者物件:
觀察者物件有一個方法叫add,用來訂閱訊息的
觀察物件有一個方法叫fire,用來發布訊息
// 定義一個觀察者物件 var Observer = $.Callbacks(); // 訂閱一個訊息,訂閱today訊息,當接收該訊息的時候,我們釋出今題的日期 Observer.add('today', function () { console.log(arguments) }) document.onclick = function () { Observer.fire('today', 'hello', 'word', 'success') }
得到結果如下:
js中觀察者模式的實現
觀者者物件必須具備兩個方法
regist 用來註冊訊息
第一個引數表示訊息的名稱
第二個引數表示回撥函式
fire 用來觸發訊息
第一個引數表示訊息的名稱
第二個引數表示傳遞資料
實現方法如下:
// 定義觀察者物件 var Observer = (function () { // 通過閉包,儲存的回撥函式對使用者來說是不可見的 var __message = {}; // 我們希望resit和fire是可以訪問的 return { /** * 用來註冊訊息 * @type 訊息的名稱 * @fn 回撥函式 **/ regist: function (type, fn) { // 將回調函式註冊到__message變數中 // 對於message訊息管道來說,我們要根據type開闢不同的儲存空間,來儲存fn(放在陣列中) // 判斷有沒有type型別的儲存空間 if (__message[type]) { // 儲存回撥函式 __message[type].push(fn); } else { // 開闢新的儲存空間儲存回撥函式 __message[type] = [fn]; } }, /** * 用來觸發訊息 * @type 訊息的名稱 * @data 傳遞的資料 **/ fire: function (type, obj) { // 適配引數 var params = { // 這裡做了優化,可以傳入作用域和引數,作用域要傳入一個物件,引數要傳入一個數組 context: (obj && obj.context) || null, args: (obj && obj.args) || [] } //把type傳入函式引數中,放在最前邊,作為第一個引數 params.args.unshift(type) // 在訊息管道中,尋找有沒有該型別的訊息,所以遍歷訊息管道 if (__message[type]) { // __message[i] 是一個數組,遍歷這個陣列並執行 for (var i = 0; i < __message[type].length; i++) { // 我們要將自定義資料傳遞進來 // __message[type][i](data) // 我們想實現jqyery的Callbacks觸發方式 // call 將引數意義列舉進來,apply將引數作為陣列傳遞 __message[type][i].apply(params.context, params.args) } } }, // 有時候我們也希望登出事件 /** * 登出事件 * @type 訊息型別 * @fn 訊息回撥函式 **/ remove: function (type, fn) { // 將fn從type的訊息佇列中刪除 if (__message[type]) { // 判斷該佇列中是否擁有fn // 從陣列中刪除一個成員用什麼方法? // 從前遍歷還是從後遍歷? // 我們從後向前遍歷,因為刪除的某一項只會影響該項後面的成員,前面的成員索引值不會影響,所以可以正常遍歷 for (var i = __message[type].length - 1; i >= 0; i--) { // 判斷該項是否是fn if (__message[type][i] === fn) { // 刪除這一項 __message[type].splice(i, 1); } } } } } })()
使用封裝的觀察者方法如下:
var hh = function () { console.log(arguments, this) } // 訂閱訊息 Observer.regist('say', hh) //定義作用域 var obj = { title: 'mm' } //觸發訊息 Observer.fire('say', { // 傳遞一個作用域,希望我們的回撥函式在obj作用域下執行 context: obj, args: ['a','b'] }) /* * 如果要登出哪個訊息裡的函式,就把哪個訊息和它對應的函式傳入進去,進行登出 * //登出這個say函式 * Observer.remove('say', hh) */
得到結果如下: