成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript 設(shè)計(jì)模式(六):觀察者模式與發(fā)布訂閱模式

bingo / 2682人閱讀

摘要:觀察者模式維護(hù)單一事件對(duì)應(yīng)多個(gè)依賴該事件的對(duì)象關(guān)系發(fā)布訂閱維護(hù)多個(gè)事件主題及依賴各事件主題的對(duì)象之間的關(guān)系觀察者模式是目標(biāo)對(duì)象直接觸發(fā)通知全部通知,觀察對(duì)象被迫接收通知。

觀察者模式(Observer)
觀察者模式:定義了對(duì)象間一種一對(duì)多的依賴關(guān)系,當(dāng)目標(biāo)對(duì)象 Subject 的狀態(tài)發(fā)生改變時(shí),所有依賴它的對(duì)象 Observer 都會(huì)得到通知。

簡(jiǎn)單點(diǎn):女神有男朋友了,朋友圈曬個(gè)圖,甜蜜宣言 “老娘成功脫單,希望你們歡喜”。各位潛藏備胎紛紛失戀,只能安慰自己你不是唯一一個(gè)。

模式特征

一個(gè)目標(biāo)者對(duì)象 Subject,擁有方法:添加 / 刪除 / 通知 Observer;

多個(gè)觀察者對(duì)象 Observer,擁有方法:接收 Subject 狀態(tài)變更通知并處理;

目標(biāo)對(duì)象 Subject 狀態(tài)變更時(shí),通知所有 Observer。

Subject 添加一系列 Observer, Subject 負(fù)責(zé)維護(hù)與這些 Observer 之間的聯(lián)系,“你對(duì)我有興趣,我更新就會(huì)通知你”。

代碼實(shí)現(xiàn)
// 目標(biāo)者類
class Subject {
  constructor() {
    this.observers = [];  // 觀察者列表
  }
  // 添加
  add(observer) {
    this.observers.push(observer);
  }
  // 刪除
  remove(observer) {
    let idx = this.observers.findIndex(item => item === observer);
    idx > -1 && this.observers.splice(idx, 1);
  }
  // 通知
  notify() {
    for (let observer of this.observers) {
      observer.update();
    }
  }
}

// 觀察者類
class Observer {
  constructor(name) {
    this.name = name;
  }
  // 目標(biāo)對(duì)象更新時(shí)觸發(fā)的回調(diào)
  update() {
    console.log(`目標(biāo)者通知我更新了,我是:${this.name}`);
  }
}

// 實(shí)例化目標(biāo)者
let subject = new Subject();

// 實(shí)例化兩個(gè)觀察者
let obs1 = new Observer("前端開發(fā)者");
let obs2 = new Observer("后端開發(fā)者");

// 向目標(biāo)者添加觀察者
subject.add(obs1);
subject.add(obs2);

// 目標(biāo)者通知更新
subject.notify();  
// 輸出:
// 目標(biāo)者通知我更新了,我是前端開發(fā)者
// 目標(biāo)者通知我更新了,我是后端開發(fā)者
優(yōu)勢(shì)

目標(biāo)者與觀察者,功能耦合度降低,專注自身功能邏輯;

觀察者被動(dòng)接收更新,時(shí)間上解耦,實(shí)時(shí)接收目標(biāo)者更新狀態(tài)。

不完美

觀察者模式雖然實(shí)現(xiàn)了對(duì)象間依賴關(guān)系的低耦合,但卻不能對(duì)事件通知進(jìn)行細(xì)分管控,如 “篩選通知”,“指定主題事件通知” 。

比如上面的例子,僅通知 “前端開發(fā)者” ?觀察者對(duì)象如何只接收自己需要的更新通知?上例中,兩個(gè)觀察者接收目標(biāo)者狀態(tài)變更通知后,都執(zhí)行了 update(),并無區(qū)分。

“00后都在追求個(gè)性的時(shí)代,我能不能有點(diǎn)不一樣?”,這就引出我們的下一個(gè)模式。進(jìn)階版的觀察者模式。“發(fā)布訂閱模式”,部分文章對(duì)兩者是否一樣都存在爭(zhēng)議。

僅代表個(gè)人觀點(diǎn):兩種模式很類似,但是還是略有不同,就是多了個(gè)第三者,因 JavaScript 非正規(guī)面向?qū)ο笳Z(yǔ)言,且函數(shù)回調(diào)編程的特點(diǎn),使得 “發(fā)布訂閱模式” 在 JavaScript 中代碼實(shí)現(xiàn)可等同為 “觀察模式”。

發(fā)布訂閱模式(Publisher && Subscriber)
發(fā)布訂閱模式:基于一個(gè)事件(主題)通道,希望接收通知的對(duì)象 Subscriber 通過自定義事件訂閱主題,被激活事件的對(duì)象 Publisher 通過發(fā)布主題事件的方式通知各個(gè)訂閱該主題的 Subscriber 對(duì)象。

發(fā)布訂閱模式與觀察者模式的不同,“第三者” (事件中心)出現(xiàn)。目標(biāo)對(duì)象并不直接通知觀察者,而是通過事件中心來派發(fā)通知。

代碼實(shí)現(xiàn)
// 事件中心
let pubSub = {
  list: {},
  subscribe: function (key, fn) {   // 訂閱
    if (!this.list[key]) {
      this.list[key] = [];
    }
    this.list[key].push(fn);
  },
  publish: function(key, ...arg) {  // 發(fā)布
    for(let fn of this.list[key]) {
      fn.call(this, ...arg);
    }
  },
  unSubscribe: function (key, fn) {     // 取消訂閱
    let fnList = this.list[key];
    if (!fnList) return false;

    if (!fn) {
      // 不傳入指定取消的訂閱方法,則清空所有key下的訂閱
      fnList && (fnList.length = 0);
    } else {
      fnList.forEach((item, index) => {
        if (item === fn) {
          fnList.splice(index, 1);
        }
      })
    }
  }
}

// 訂閱
pubSub.subscribe("onwork", time => {
  console.log(`上班了:${time}`);
})
pubSub.subscribe("offwork", time => {
  console.log(`下班了:${time}`);
})
pubSub.subscribe("launch", time => {
  console.log(`吃飯了:${time}`);
})

// 發(fā)布
pubSub.publish("offwork", "18:00:00"); 
pubSub.publish("launch", "12:00:00");

// 取消訂閱
pubSub.unSubscribe("onwork");

發(fā)布訂閱模式中,訂閱者各自實(shí)現(xiàn)不同的邏輯,且只接收自己對(duì)應(yīng)的事件通知。實(shí)現(xiàn)你想要的 “不一樣”。

DOM 事件監(jiān)聽也是 “發(fā)布訂閱模式” 的應(yīng)用:

let loginBtn = document.getElementById("#loginBtn");

// 監(jiān)聽回調(diào)函數(shù)(指定事件)
function notifyClick() {
    console.log("我被點(diǎn)擊了");
}

// 添加事件監(jiān)聽
loginBtn.addEventListener("click", notifyClick);
// 觸發(fā)點(diǎn)擊, 事件中心派發(fā)指定事件
loginBtn.click();             

// 取消事件監(jiān)聽
loginBtn.removeEventListener("click", notifyClick);

發(fā)布訂閱的通知順序:

先訂閱后發(fā)布時(shí)才通知(常規(guī))

訂閱后可獲取過往以后的發(fā)布通知 (QQ離線消息,上線后獲取之前的信息)

流行庫(kù)的應(yīng)用

jQuery 的 ontrigger,$.callback();

Vue 的雙向數(shù)據(jù)綁定;

Vue 的父子組件通信 $on/$emit

jQuery 的 $.Callback()

jQuery 的 $.Callback() 更像是觀察者模式的應(yīng)用,不能更細(xì)粒度管控。

function notifyHim(value) {
 console.log("He say " + value);
}

function notifyHer(value) {
 console.log("She say " + value);
}

$cb = $.Callbacks();    // 聲明一個(gè)回調(diào)容器:訂閱列表 

$cb.add(notifyHim);     // 向回調(diào)列表添加回調(diào):訂閱
$cb.add(notifyHer);     // 向回調(diào)列表添加回調(diào):訂閱

$cb.fire("help");       // 調(diào)用所有回調(diào): 發(fā)布
Vue 的雙向數(shù)據(jù)綁定

利用 Object.defineProperty() 對(duì)數(shù)據(jù)進(jìn)行劫持,設(shè)置一個(gè)監(jiān)聽器 Observer,用來監(jiān)聽數(shù)據(jù)對(duì)象的屬性,如果屬性上發(fā)生變化了,交由 Dep 通知訂閱者 Watcher 去更新數(shù)據(jù),最后指令解析器 Compile 解析對(duì)應(yīng)的指令,進(jìn)而會(huì)執(zhí)行對(duì)應(yīng)的更新函數(shù),從而更新視圖,實(shí)現(xiàn)了雙向綁定。

Observer (數(shù)據(jù)劫持)

Dep (發(fā)布訂閱)

Watcher (數(shù)據(jù)監(jiān)聽)

Compile (模版編譯)

關(guān)于 Vue 雙向數(shù)據(jù)綁定原理,可自行參考其它文章,或推薦本篇 《 vue雙向數(shù)據(jù)綁定原理》。

Vue源碼傳送門

Vue 的父子組件通信

Vue 中,父組件通過 props 向子組件傳遞數(shù)據(jù)(自上而下的單向數(shù)據(jù)流)。父子組件之間的通信,通過自定義事件即 $on , $emit 來實(shí)現(xiàn)(子組件 $emit,父組件 $on)。

原理其實(shí)就是 $emit 發(fā)布更新通知,而 $on 訂閱接收通知。Vue 中還實(shí)現(xiàn)了 $once(一次監(jiān)聽),$off(取消訂閱)。

// 訂閱
vm.$on("test", function (msg) {
    console.log(msg)
})

// 發(fā)布
vm.$emit("test", "hi")

Vue源碼傳送門

Vue文檔傳送門

優(yōu)勢(shì)

對(duì)象間功能解耦,弱化對(duì)象間的引用關(guān)系;

更細(xì)粒度地管控,分發(fā)指定訂閱主題通知

不完美

對(duì)間間解耦后,代碼閱讀不夠直觀,不易維護(hù);

額外對(duì)象創(chuàng)建,消耗時(shí)間和內(nèi)存(很多設(shè)計(jì)模式的通病)

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

類似點(diǎn)

都是定義一個(gè)一對(duì)多的依賴關(guān)系,有關(guān)狀態(tài)發(fā)生變更時(shí)執(zhí)行相應(yīng)的通知。

區(qū)別點(diǎn)

發(fā)布訂閱模式更靈活,是進(jìn)階版的觀察者模式,指定對(duì)應(yīng)分發(fā)。

觀察者模式維護(hù)單一事件對(duì)應(yīng)多個(gè)依賴該事件的對(duì)象關(guān)系;

發(fā)布訂閱維護(hù)多個(gè)事件(主題)及依賴各事件(主題)的對(duì)象之間的關(guān)系;

觀察者模式是目標(biāo)對(duì)象直接觸發(fā)通知(全部通知),觀察對(duì)象被迫接收通知。發(fā)布訂閱模式多了個(gè)中間層(事件中心),由其去管理通知廣播(只通知訂閱對(duì)應(yīng)事件的對(duì)象);

觀察者模式對(duì)象間依賴關(guān)系較強(qiáng),發(fā)布訂閱模式中對(duì)象之間實(shí)現(xiàn)真正的解耦。

對(duì)象屬性數(shù)據(jù)攔截方式:

Object.defineProperty() 屬性描述符;

ES6 Class set ;

ES6 Proxy 代理;

參考文章:

談?wù)動(dòng)^察者模式和發(fā)布訂閱模式

原生JavaScript實(shí)現(xiàn)觀察者模式

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

vue雙向數(shù)據(jù)綁定原理

本文首發(fā)Github,期待Star!
https://github.com/ZengLingYong/blog

作者:以樂之名
本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請(qǐng)指明出處。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105523.html

相關(guān)文章

  • JavaScript設(shè)計(jì)模式發(fā)布-訂閱模式察者模式)-Part1

    摘要:設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記。發(fā)布訂閱模式又叫觀察者模式,它定義了對(duì)象之間的一種一對(duì)多的依賴關(guān)系。附設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式數(shù)據(jù)結(jié)構(gòu)和算法系列棧隊(duì)列優(yōu)先隊(duì)列循環(huán)隊(duì)列設(shè)計(jì)模式系列設(shè)計(jì)模式之策略模式 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。 發(fā)布-訂閱模式又叫觀察者模式,它定義了對(duì)象之間的一種一對(duì)多的依賴關(guān)系。當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴它的對(duì)象都將得到通知。 例...

    muzhuyu 評(píng)論0 收藏0
  • JavaScript 發(fā)布-訂閱模式

    摘要:發(fā)布訂閱模式訂閱者把自己想訂閱的事件注冊(cè)到調(diào)度中心,當(dāng)發(fā)布者發(fā)布該事件到調(diào)度中心,也就是該事件觸發(fā)時(shí),由調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊(cè)到調(diào)度中心的處理代碼。 發(fā)布-訂閱模式,看似陌生,其實(shí)不然。工作中經(jīng)常會(huì)用到,例如 Node.js EventEmitter 中的 on 和 emit 方法;Vue 中的 $on 和 $emit 方法。他們都使用了發(fā)布-訂閱模式,讓開發(fā)變得更加高效方便。 一...

    13651657101 評(píng)論0 收藏0
  • JavaScript設(shè)計(jì)模式發(fā)布-訂閱模式察者模式)-Part2

    摘要:設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記。看此文章前,建議先看設(shè)計(jì)模式之發(fā)布訂閱模式觀察者模式在中,已經(jīng)介紹了什么是發(fā)布訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布訂閱模式。 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》讀書筆記。 看此文章前,建議先看JavaScript設(shè)計(jì)模式之發(fā)布-訂閱模式(觀察者模式)-Part1 在Part1中,已經(jīng)介紹了什么是發(fā)布-訂閱模式,同時(shí),也實(shí)現(xiàn)了發(fā)布-訂閱模式。但是,就Part1...

    Charlie_Jade 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<