js設計模式(觀察者迷郑、中介者模式)

觀察者模式(訂閱發(fā)布模式)

通過 訂閱-發(fā)布 (subscribe-publish) 模型嘿般,消除組件之間雙向依賴
消息的 發(fā)布者 (subject) 不需要知道 觀察者 (observer) 的存在
兩者只需要約定消息的格式(如何訂閱靴姿、如何發(fā)布),就可以通信
我們先看一段代碼

    document.querySelector(".div").innerHTML="我是周杰倫"
    document.getElementsByClassName("div")[0].innerHTML="我是周杰倫"
    $(".div").html("我是周杰倫1")

這種操作DOM的寫法看起來也很簡單拐辽,沒什么大問題拣挪,但是如果有N多DOM的時候數(shù)據(jù)需要動態(tài)換,則這樣的代碼就非常難以維護俱诸,甚至取名字都會成為程序員頭疼的事情菠劝。所以有些人就想是不是可以數(shù)據(jù)驅(qū)動。數(shù)據(jù)變化反饋到DOM上乙埃,DOM變化反饋到數(shù)據(jù)上闸英,形成這樣的雙向數(shù)據(jù)流的格式會比現(xiàn)在的操作DOM會更加方便。
所以我們有了雙向數(shù)據(jù)流VUE 介袜,Angular,有了單向數(shù)據(jù)流的 CYCLE 和 REACT

雙向數(shù)據(jù)綁定(two-way data binding)

意味著 UI 層所呈現(xiàn)的內(nèi)容和 Model 層的數(shù)據(jù)動態(tài)地綁定在一起了出吹,其中一個發(fā)生了變化遇伞,就會立刻反映在另一個上。比如用戶在前端頁面的表單控件中輸入了一個值捶牢,Model 層對應該控件的變量就會立刻更新為用戶所輸入的值鸠珠;反之亦然,如果 Modal 層的數(shù)據(jù)有變化秋麸,變化后的數(shù)據(jù)也會立刻反映至 UI 層渐排。

單向數(shù)據(jù)流(one-way data flow)

意味著只有 Model 層才是單一數(shù)據(jù)源(single source of truth)。UI 層的變化會觸發(fā)對應的消息機制灸蟆,告知 Model 層用戶的目的(對應 React 的 store)驯耻。只有 Model 層才有更改應用狀態(tài)的權(quán)限,這樣一來炒考,數(shù)據(jù)永遠都是單向流動的可缚,也就更容易了解應用的狀態(tài)是如何變化的。

言歸正傳 那么我們的訂閱發(fā)布到底是什么呢

其實就是字面意思 你訂閱了我的信息 等我更新了 我就給你一個反饋

大家都知道 VUE中有一個事件機制 業(yè)就是 $ON 和 $EMMIT 下面我們來看看 分析分析

$on

  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
    const vm: Component = this

    /*如果是數(shù)組的時候斋枢,則遞歸$on帘靡,為每一個成員都綁定上方法*/
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        this.$on(event[i], fn)
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn)
      // optimize hook:event cost by using a boolean flag marked at registration
      // instead of a hash lookup
      /*這里在注冊事件的時候標記bool值也就是個標志位來表明存在鉤子,而不需要通過哈希表的方法來查找是否有鉤子瓤帚,這樣做可以減少不必要的開銷描姚,優(yōu)化性能涩赢。*/
      if (hookRE.test(event)) {
        vm._hasHookEvent = true
      }
    }
    return vm
  }

$emmit

Vue.prototype.$emit = function (event: string): Component {
    const vm: Component = this
    if (process.env.NODE_ENV !== 'production') {
      const lowerCaseEvent = event.toLowerCase()
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          `Event "${lowerCaseEvent}" is emitted in component ` +
          `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
          `Note that HTML attributes are case-insensitive and you cannot use ` +
          `v-on to listen to camelCase events when using in-DOM templates. ` +
          `You should probably use "${hyphenate(event)}" instead of "${event}".`
        )
      }
    }
    let cbs = vm._events[event]
    if (cbs) {
      /*將類數(shù)組的對象轉(zhuǎn)換成數(shù)組*/
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      /*遍歷執(zhí)行*/
      for (let i = 0, l = cbs.length; i < l; i++) {
        cbs[i].apply(vm, args)
      }
    }
    return vm
  }

我們再來看看使用

Vue.$on("someFun",(data)=>{ ...dosomething})
Vue.$emmit("someFun",this.data)

在JS中怎么處理呢

//每一個on都會這樣 
let callback[key].push(callbackFunction)
//在emmit的時候 
  callback[key].forEach(item=>item(data))

Vue通過設定對象屬性的 setter/getter 方法來監(jiān)聽數(shù)據(jù)的變化,通過getter進行依賴收集轩勘,而每個setter方法就是一個觀察者谒主,在數(shù)據(jù)變更的時候通知訂閱者更新視圖。

小結(jié): 發(fā)布訂閱模式就是 把觀察者放在一個Array中 然后在被觀察者發(fā)生改變時通知所有的觀察者赃阀,使用回調(diào)函數(shù)的方式通知霎肯。

中介者模式

通過設置 消息中心 (message center),避免組件之間直接依賴
所有的 協(xié)同者 (colleague) 只能通過 中介者 (mediator) 進行通信榛斯,
而相互之間不知道彼此的存在
當各個組件的消息出現(xiàn)循環(huán)時观游,消息中心可以消除組件之間的依賴混亂

var mediator = (function() {
    var topics = {},
        subUid = -1;
    var publish = function(topic, args) {
        if (!topics[topic]) {
            return false;
        }

        var subscribers = topics[topic],
            len = subscribers ? subscribers.length : 0;
        while (len--) {
            subscribers[len].func(topic, args);
        }

        return true;
    };

    var subscribe = function(topic, func) {
        if (!topics[topic]) {
            topics[topic] = [];
        }

        var token = (++subUid).toString();
        topics[topic].push({
            token: token,
            func: func
        });

        return token;
    };

    return {
        publish: publish,
        subscribe: subscribe,
        installTo: function(obj) {
            obj.publish = publish;
            obj.subscribe = subscribe;
        }
    }
}());


// 具體應用
var mod1 = {
    run: function(arg) {
        console.log('mod1 received ' + arg);
    }
};
var mod2 = {};
var topic = 'myTopic';
mediator.installTo(mod1);
mediator.installTo(mod2);
// mod1訂閱消息
mod1.subscribe(topic, function(t, arg) {
    mod1.run(arg);
});
// mod2發(fā)布消息
mod2.publish(topic, 'data');

總結(jié)
觀察者模式和中介者模式看起來非常的相似,核心原理都是一樣的驮俗,這里有一個細微的差別懂缕,中介者模式注重狀態(tài)告知,觀察者模式側(cè)重組件數(shù)據(jù)通信王凑,其實我們這里完全使用觀察者模式也可以實現(xiàn)狀態(tài)告知搪柑,不過 但觀察者是分發(fā)性的,所有的觀察者都會受到信息索烹,而且中介者則是單一的工碾,對象的通信由中介者處理。
中介者模式 一般通過 觀察者模式 實現(xiàn)
協(xié)同者 作為 發(fā)布者百姓,中介者 作為 觀察者
協(xié)同者 發(fā)布消息 -> 中介者 收到并處理消息 -> 中介者 直接發(fā)送消息給 協(xié)同者
協(xié)同者 不依賴于 中介者
當組件之間依賴關(guān)系簡單時渊额,可以直接使用 觀察者模式
當組件之間依賴關(guān)系復雜是,需要借助 中介者模式 梳理關(guān)系

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末垒拢,一起剝皮案震驚了整個濱河市旬迹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌求类,老刑警劉巖奔垦,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尸疆,居然都是意外死亡椿猎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門仓技,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸵贬,“玉大人,你說我怎么就攤上這事脖捻±疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵地沮,是天一觀的道長嗜浮。 經(jīng)常有香客問我羡亩,道長,這世上最難降的妖魔是什么危融? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任畏铆,我火速辦了婚禮,結(jié)果婚禮上吉殃,老公的妹妹穿的比我還像新娘辞居。我一直安慰自己,他們只是感情好蛋勺,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布瓦灶。 她就那樣靜靜地躺著,像睡著了一般抱完。 火紅的嫁衣襯著肌膚如雪贼陶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天巧娱,我揣著相機與錄音碉怔,去河邊找鬼。 笑死禁添,一個胖子當著我的面吹牛撮胧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播上荡,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼趴樱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酪捡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纳账,失蹤者是張志新(化名)和其女友劉穎逛薇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疏虫,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡永罚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卧秘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呢袱。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖翅敌,靈堂內(nèi)的尸體忽然破棺而出羞福,到底是詐尸還是另有隱情,我是刑警寧澤蚯涮,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布治专,位于F島的核電站卖陵,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏张峰。R本人自食惡果不足惜泪蔫,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喘批。 院中可真熱鬧撩荣,春花似錦、人聲如沸饶深。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粥喜。三九已至凸主,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間额湘,已是汗流浹背卿吐。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锋华,地道東北人嗡官。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像毯焕,于是被迫代替她去往敵國和親衍腥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355