JS设计模式之发布订阅模式

经常会有面试题问发布订阅模式,很多童鞋都一脸懵逼,根本不知道在问什么,甚至觉得自己根本用不上这玩意。

其实,市面上的各大框架的事件处理,特别是组件通讯都基于发布订阅模式实现,比如vue的vuex,reace的redux。

那么什么是发布订阅模式?

发布订阅模式又叫观察者模式,它定义对象之间的一种一对多的依赖关系,当一个抽象的状态发生改变时,所有依赖它的对象都将得到通知。

下面我们用一个现实中例子来讲一下它们到底是什么。

小王去售楼部买房,但是该楼盘已售罄,于是售楼MM就告诉小王,我给你登记一下你的需求(订阅),等有房的时候我打电话通知你(发布)。

所以这就是一个发布订阅的简单例子,购房者只需要订阅自己的需求,当需求更新的时候,就会通知订阅者。

那么下面我们手写js,实现一个简单的发布订阅模式。

 var salesOffices = {} // 定义售楼处

    salesOffices.clientList = [] // 缓存列表,存放订阅者回调函数

    // 订阅消息
    salesOffices.listen = function (key, fn) {
      if (!this.clientList[key]) {
        this.clientList[key] = [] // 如果没有订阅过该类型,则创建缓存列表
      }
      this.clientList[key].push(fn) // 订阅的消息存入缓存列表
    }

    // 发布消息
    salesOffices.trigger = function () {
      var key = Array.prototype.shift.call(arguments), // 取出消息类型 arguments为类数组
        fns = this.clientList[key]; // 取出该类型对应的函数集合

      if (!fns || fns.length === 0) {
        return false // 如果没有订阅消息 则返回
      }

      for (var i = 0, fn; fn = fns[i++];) {
        fn.apply(this, arguments) // 代入参数arguments 执行
      }
    }

    // 取消订阅
    salesOffices.remove = function (key, fn) {
      var fns = this.clientList[key]

      if (!fns) {
        return false // 如果没有订阅消息 则返回
      }

      if (!fn) {
        fns && (fns.length = 0) // 如果没有传入具体回调函数,则表示取消该key下的所有订阅
      } else {
        for (var l = fns.length - 1; l >= 0; l--) {
          var _fn = fns[l]
          if (_fn === fn) {
            fns.splice(l, i) // 删除订阅回调函数
          }
        }
      }
    }

// demo测试
salesOffices.listen('square88', function(price) {
      console.log(`价格=${price}`)
    })

salesOffices.trigger('square88', 200000)

以上版本,我们需要提前定义一个售楼处,但其实我们买房子的时候可以不用去售楼处,直接去中介那就可以了,所以我们对其进行改造,写一个全局的发布订阅模式

var Event = (function () {
      var clientList = {},
        listen,
        trigger,
        remove;

      listen = function (key, fn) {
        if (!clientList[key]) {
          clientList[key] = []
        }
        clientList[key].push(fn)
      }

      trigger = function () {
        var key = Array.prototype.shift.call(arguments),
          fns = clientList[key];
        if (!fns || fns.length === 0) {
          return false
        }
        for (var i = 0, fn; fn = fns[i++];) {
          fn.apply(this, arguments)
        }
      }

      remove = function (key, fn) {
        var fns = clientList[key]
        if (!fns) {
          return false
        }

        if (!fn) {
          fns && (fns.length = 0)
        } else {
          for (var l = fns.length - 1; l >= 0; l--) {
            var _fn = fns[l]
            if (_fn === fn) {
              fns.splice(l, 1)
            }
          }
        }
      }

      return {
        listen,
        trigger,
        remove
      }
    })()

    Event.listen('square88', function (price) {
      console.log(`价格是${price}`)
    })

    Event.trigger('square88', 10000)

总结:

通过对发布订阅模式的学习,我们很清楚能感受到它的优点。一为时间上的解耦,二为对象之间的解耦。但是它的缺点也很明显。每次创建订阅都会消耗时间和内存,如果该订阅一直没有触发,那么它依然会存在内存中,而且通过这种模式弱化了对象和对象间的联系,这让如果出了BUG,如何找到这个BUG也是让人头疼的事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值