觀察者模式的使用介紹

javascript觀察者模式

介紹

觀察者模式又稱發佈-訂閱模式,它定義對象間的一種一對多的依賴關係,當一個對象發生改變的時候,所依賴它的對象都能獲得通知。例如:咱們訂閱了一個欄目,當欄目有新文章的時候,它會自動通知全部訂閱它的人。javascript

特色

  • 發佈 & 訂閱
  • 一對多

優勢

  • 低耦合,觀察者和觀察目標都是抽象出來,容易擴展和重用
  • 觸發(通信)機制,由觀察目標通知全部觀察它的人

缺點

  • 一個觀察目標下可能存在不少的觀察者,那麼當觀察目標須要通知全部觀察者的時候會花不少時間
  • 觀察者和觀察目標之間若是存在依賴的話,可能會發生循環調用,進入死循環致使系統崩潰
  • 觀察者模式沒有相應的機制讓觀察者知道觀察目標是如何發生變化,僅僅知道觀察目標發生了變化
  • 觀察目標可能將一些無用的更新發送出去

簡單例子

  • 例子:A,B,C三我的都關注了某一個電臺,當電臺發佈新內容的時候通知A,B,C三我的。
  • 構思:具體的構思中,咱們能夠知道電臺做爲發佈者,它有了新的內容,須要向ABC這三個訂閱者推送他的最新的消息。那麼咱們就能夠知道電臺這個發佈者須要包含新的更新內容以及有哪些訂閱者訂閱了它。
  • 代碼實現:
    簡單的代碼實現:html

    class Radio {
      constructor() {
        this.state = 0;
        this.observers = [];
      }
    
      setState(state) {
        this.state = state;
        this.notifyingObservers();
      }
    
      addObserver(observer) {
        this.observers.push(observer);
      }
    
      notifyingObservers() {
        const state = this.state;
        this.observers.forEach(observer => {
          observer.update(state);
        });
      }
    }
    
    class People {
      constructor(name) {
        this.name = name;
      }
    
      update(content) {
        console.log(`我是${this.name},我接受到更新了,更新內容:${content}`);
      }
    }
    
    // 建立訂閱者
    const peopleA = new People('小A');
    const peopleB = new People('小B');
    const peopleC = new People('小C');
    
    // 添加發布者
    const radio = new Radio();
    
    // 訂閱
    radio.addObserver(peopleA);
    radio.addObserver(peopleB);
    radio.addObserver(peopleC);
    
    // 發佈者發佈
    radio.setState('十月份最熱歌單發佈了');
    radio.setState('十一月份最新原創歌單發佈了');
  • 解讀:
  1. 抽象了一個發佈者(Radio),它有更新內容(setState)、添加訂閱者(addObserver)、以及觸發全部訂閱者(notifyingObservers);
  2. 抽象了一個訂閱者(People),他有本身的我的信息(如:name),以及接受到通知後所須要執行的動做(updata);
  3. 當咱們每次更新消息的時候出發notifyingObservers方法,將全部的observersupdate都觸發了

固然,實際上,咱們上的每個訂閱者都有這個update,當這個update不知足功能需求的時候,咱們一樣能夠將實例出來的訂閱者單獨設置update; 如:java

peopleA.update = function(content) {
    // 新代碼
  }

以上就是一個簡單的觀察者模式的例子node

場景延伸

  1. 網頁頁面事件
  • 代碼案例:react

    <button id="btn">點擊</button>
    
    <script>
      $('#btn').click(function() {
          console.log('btn被點擊');
      })
    </script>
  • 解讀:能夠理解成函數訂閱了$('#btn')的click事件,當$('#btn')的click被咱們點擊觸發,函數收到觸發信息,並自執行。 那麼這個函數就是觀察者(訂閱者),$('#btn')的click事件就是觀察目標(發佈者)。
  1. Promise
  • 代碼案例:git

    function loadImage(url) {
      return new Promise(function(resolve, reject) {
        let image = document.createElement('img');
        image.onload = function () {
          resolve(image);
        }
        image.onerror = function () {
          reject('圖片加載失敗');
        }
        image.src = url;
      });
    }
    const src = 'http://imgsrc.baidu.com/image/c0%3Dpixel_huitu%2C0%2C0%2C294%2C40/sign=ad13ee4af0f2b211f0238d0ea3f80054/2e2eb9389b504fc26849383ceedde71190ef6df1.jpg'
    const img = loadImage(src);
    img.then(function (img) {
      console.log('width', img.width);
      return img
    }).then(function (img) {
      console.log('height', img.height);
    });
  • 解讀:promise的resolve是then的執行者,當promise的狀態發生改變後(resolve的時候狀態從」未完成「變爲」成功「),一一執行then下的方法,那麼這些then能夠說是promise的觀察者,當這個promise被resolve的時候,全部的觀察獲得了通知。
  • 關於promise內部的觀察者模式能夠參數https://github.com/xieranmaya/blog/issues/3這篇文檔。

promise.then會把內部的函數添加到一個callback的數組內,等異步執行完成以後在進行一次調用該函數。每個.then會返回一個新的promise。github

  1. js的事件觸發器(自定義事件)
  • 代碼案例:數組

    class EventEmitter() {
      constructor() {
        this.events = {};
      }
    
      // 訂閱事件
      on(type, listener) {
        if (!this.events) { this.events = Object.create(null); }
    
        if (this.events[type]) {
          this.events[type].push(listener)
        } else {
          this.events[type] = [listener];
        }
      }
    
      // 觸發執行
      emit(type, ...args) {
        if (this.events[type]) {
          this.events[type].forEach(fn => fn.call(this, ...args));
        }
      }
    
      // 解綁
      off(type, listener) {
        id (this.events[type]) {
          this.events[type] = this.events[type].filter(fn => {
            return fn !== listener;
          });
        }
      }
    }
    
    const myEmitter = new EventEmitter();
    myEmitter.on('log', function() {console.log('111111')});
    myEmitter.emit('log');
  • 解讀:這個就和第一個案例有點類似,咱們在jq中見過這樣的頁面事件寫法:promise

    $('id').on('click', function() {
      // 事件代碼
    });

    這個就是一種事件觸發器,在nodejs裏面大量採用了事件觸發器的方法。詳情能夠去看nodejs裏面的EventEmitter方法,就能夠大致理解他的機制了。
    同理,咱們也能夠明白react裏面的生命週期等mvvm框架,裏面大量採用了觀察者模式。它們都是定義了一個個鉤子,等狀態達到的時候我就觸發相對應的鉤子,執行相對應的代碼。框架

總結

總之,觀察者模式在javascript中的使用是很是普遍的。其低耦合的特色方便在多人開發的複雜項目中,能提升效率,使代碼的維護性大大提高。

相關文章
相關標籤/搜索