js自定義事件

事件是一種叫做觀察者的設計模式渐溶,這是一種創(chuàng)建松散耦合代碼的技術翔悠。對象可以發(fā)布事件,用來表示在該對象生命周期中有某個有趣的時刻到了元莫。然后其他對象可以觀察該對象,等待這些有趣的時刻到來蝶押,并通過運行代碼來響應踱蠢。
觀察者模式由兩類對象組成:主體觀察者。主體負責發(fā)布事件棋电,同時觀察者通過訂閱這些事件來觀察該主體茎截。該模式的一個關鍵概念是主體不知道觀察者的任何事情,也就是說它可以獨自存在并正常運作即使觀察者不存在赶盔。從另一方面來說企锌,觀察者知道主體并能注冊時間的回調(diào)函數(shù)(事件處理程序)。

上面是紅寶書(《JavaScript高級程序設計(第三版)》)對自定義事件的描述于未。并且撕攒,它認為自定義事件就是觀察者模式陡鹃。觀察者模式有時候也叫發(fā)布/訂閱模式
但實際上打却,觀察者模式和發(fā)布/訂閱模式有一點小小的不同杉适,即發(fā)布/訂閱模式有一個調(diào)度中心,對事件進行統(tǒng)一處理柳击。而觀察者模式會將處理事件的代碼分散在每個主體中猿推。發(fā)布/訂閱模式可以說是升級版觀察者模式。
紅寶書中實現(xiàn)自定義事件的代碼實際上是發(fā)布/訂閱模式捌肴。

什么時候用


個人認為蹬叭,觀察者模式主要解決的是模塊之間的通信問題。你有兩個類状知,當一個類中某個屬性發(fā)生變化的時候秽五,你要通知另一個類,讓它執(zhí)行對應的代碼饥悴,這個時候就可以使用觀察者模式坦喘。觀察者模式也叫消息機制。

原理


不管是觀察者模式還是發(fā)布/訂閱模式西设,原理都是一樣的瓣铣。
看到這個名字,我一開始以為贷揽,當消息產(chǎn)生時棠笑,主體將消息內(nèi)容通知給觀察者,觀察者執(zhí)行對應的函數(shù)禽绪。但實際上蓖救,實現(xiàn)的思路剛好相反。
執(zhí)行操作的是主體而不是觀察者印屁。觀察者起到的作用只是為消息注冊函數(shù)循捺,當主體發(fā)布消息時,會執(zhí)行注冊在該消息上的所有函數(shù)雄人。
回想一下瀏覽器中的事件(click巨柒,keydown),我們做的是給每個事件綁定響應的操作函數(shù)柠衍,我們寫的js屬于觀察者洋满。當事件發(fā)生時,瀏覽器就會執(zhí)行我們綁定的函數(shù)珍坊,其實原理是一樣的牺勾。

實現(xiàn)


發(fā)布/訂閱模式

var Observer = (function() {
  // 消息容器,用來存放消息和要對應的操作函數(shù)
  var __messages = {}
  return {
    // 注冊消息接口
    regist: function(type, fn) {
      // 當消息不存在時阵漏,創(chuàng)建一個該消息類型驻民,將回調(diào)函數(shù)推入執(zhí)行隊列中
      if (typeof __messages[type] === 'undefined') {
        __messages[type] = [fn]
      } else {
        // 消息類型存在時翻具,將回調(diào)函數(shù)推入執(zhí)行隊列中
        __messages[type].push(fn)
      }
    },

    // 發(fā)布消息接口
    fire: function(type, args) {
      // 當消息不存在時直接返回
      if (!__messages[type]) 
        return

      args = args || {}
      // 依次執(zhí)行消息對應的動作隊列
      for (var i = 0, len = __messages[type].length; i < len; i++) {
        __messages[type][i].call(this, args)
      }
    },

    // 移除消息接口
    remove: function(type, fn) {
      if (__messages[type] instanceof Array) {
        // 從后面開始遍歷,如果存在該動作則將其移除
        for (var i = __messages[type].length - 1; i >= 0; i--) {
          __messages[type][i] === fn && __messages[type].splice(i, 1)
        }
      }
    }
  }
})()

// 在觀察者中注冊消息
Observer.regist('test', function(args){
  console.log(args.msg)  // 消息內(nèi)容
})

// 主體發(fā)布消息
Observer.fire('test', { msg: '消息內(nèi)容' })

通過單例模式創(chuàng)建的Observer對象回还,就是發(fā)布/訂閱模式中的調(diào)度中心裆泳。私有變量__messages保存了消息類型及其對應的動作數(shù)組。觀察者通過Observer.regist接口給消息注冊動作柠硕,主體(發(fā)布者)通過Observer.fire發(fā)布消息工禾,執(zhí)行該消息的所有動作。通過Observer.remove方法可以移除對應動作蝗柔。
可以說闻葵,這個一個簡易的addEventListener的實現(xiàn)。
紅寶書中的實現(xiàn)與上述代碼的區(qū)別只是癣丧,它使用了構(gòu)造函數(shù)-原型混合模式創(chuàng)建Observer對象槽畔,可以實例化多個調(diào)度中心。

觀察者模式

// 觀察者列表
function ObserverList() {
  this.observerList = []
  if (typeof this.add !== 'function') {
    ObserverList.prototype.add = function(obj) {
      return this.observerList.push(obj)
    }
    ObserverList.prototype.count = function() {
      return this.observerList.length
    }
    ObserverList.prototype.get = function(index) {
      if (index > -1 && index < this.observerList.length) {
        return this.observerList[index]
      }
    }
    ObserverList.prototype.remove = function(observer) {
      this.observerList.filter(function(item){
        return item !== observer
      })
    }
  }
}

// 主體(消息發(fā)布者)
function Subject() {
  this.observers = new ObserverList()
  if (typeof this.addObserver !== 'function') {
    Subject.prototype.addObserver = function(observer) {
      this.observers.add(observer)
    }
    Subject.prototype.removeObserver = function(observer) {
      this.observers.remove(observer)
    }
    Subject.prototype.notify = function(args) {
      var count = this.observers.count()
      for (var i = 0; i < count; i++) {
        this.observers.get(i).update(args)
      }
    }
  }
}

// 觀察者
function Observer() {
  this.update = function(){
    // ...
  }
}

主體內(nèi)部需要維護一個觀察者列表胁编,將需要觀察主體變化的對象加入這個列表厢钧。當消息發(fā)生時,可以調(diào)用notify方法通知所有觀察者(即調(diào)用觀察者的update方法)嬉橙。
可以看出早直,在每個主體中都要維護一個觀察者列表,造成代碼冗余憎夷。發(fā)布消息時,需要調(diào)用觀察者的方法昧旨,主體和觀察者耦合度高拾给。上面的代碼中,只有update一種消息類型兔沃,如果想要多種消息類型蒋得,主體需要定義多個函數(shù),十分麻煩乒疏。
因此额衙,發(fā)布/訂閱模式更加常用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怕吴,一起剝皮案震驚了整個濱河市窍侧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌转绷,老刑警劉巖伟件,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異议经,居然都是意外死亡斧账,警方通過查閱死者的電腦和手機谴返,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咧织,“玉大人嗓袱,你說我怎么就攤上這事∠熬睿” “怎么了渠抹?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毯炮。 經(jīng)常有香客問我逼肯,道長,這世上最難降的妖魔是什么桃煎? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任篮幢,我火速辦了婚禮,結(jié)果婚禮上为迈,老公的妹妹穿的比我還像新娘三椿。我一直安慰自己,他們只是感情好葫辐,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布搜锰。 她就那樣靜靜地躺著,像睡著了一般耿战。 火紅的嫁衣襯著肌膚如雪蛋叼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天剂陡,我揣著相機與錄音狈涮,去河邊找鬼。 笑死鸭栖,一個胖子當著我的面吹牛歌馍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晕鹊,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼松却,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了溅话?” 一聲冷哼從身側(cè)響起晓锻,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎飞几,沒想到半個月后带射,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡循狰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年窟社,在試婚紗的時候發(fā)現(xiàn)自己被綠了券勺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡灿里,死狀恐怖关炼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匣吊,我是刑警寧澤儒拂,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站色鸳,受9級特大地震影響社痛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜命雀,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一蒜哀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吏砂,春花似錦撵儿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匈织,卻和暖如春浪默,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缀匕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工纳决, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弦追。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓岳链,卻偏偏與公主長得像花竞,于是被迫代替她去往敵國和親劲件。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 2018年2月28日 背景 業(yè)務開發(fā)中遇到JS消息沒被成功接收的問題约急。順帶復習一下 JS 事件零远。 為實現(xiàn)不同模塊間...
    HeyDelilah閱讀 1,458評論 0 0
  • 不管是angular還是vue都暴露了自定義事件的接口,$emit $on $broadcase等厌蔽,現(xiàn)在我們嘗試自...
    lvzhiyi閱讀 976評論 2 1
  • 一牵辣、函數(shù)自定義事件 1、字面量式 2奴饮、原型鏈式 二纬向、DOM 自定義事件
    R_X閱讀 184評論 0 0
  • obsession_me閱讀 920評論 0 0
  • 馬還在首都 挺想馬的择浊,翻看照片 突發(fā)奇想去魔都 說出去的話 潑出去的水 所以還是去 半夜了不睡覺 發(fā)狀態(tài) 馬很白 ...
    ninvxv閱讀 83評論 0 0