基于 javascript 设计模式一书中发布订阅模式的个人理解

发布可以早于订阅吗?

可以
例如在 js 设计模式一书中的例子,只截取了关键的代码

Event.trigger('click', 1)

Event.listen('click', function(a) {
  console.log(a) // 输出: 1
})

var Event = (function() {
  var global = this,
    Event,
    _default = 'default'
  Event = (function() {
    var _listen,
      _trigger,
      _remove,
      _shift = Array.prototype.shift,
      _unshift = Array.prototype.unshift,
      _create,
      each = function(ary, fn) {
        var ret
        for (var i = 0, l = ary.length; i < l; i++) {
          var n = ary[i]
          ret = fn.call(n, i, n)
        }
        return ret
      }

    // 内部的listen
    _listen = function(key, fn, cache) {
      if (!cache[key]) {
        cache[key] = []
      }
      cache[key].push(fn)
    }

    // 内部的listen
    _trigger = function() {
      console.log('_trigger', arguments)

      var cache = _shift.call(arguments),
        key = _shift.call(arguments),
        args = arguments,
        _self = this,
        ret,
        stack = cache[key]
      if (!stack || !stack.length) {
        return
      }
      return each(stack, function() {
        return this.apply(_self, args)
      })
    }

    _create = function(namespace) {
      var cache = {},
        offlineStack = [], // 离线事件
        ret = {
          listen: function(key, fn, last) {
            _listen(key, fn, cache)
            if (offlineStack === null) {
              return
            }
            if (last === 'last') {
              offlineStack.length && offlineStack.pop()()
            } else {
              each(offlineStack, function() {
                this()
              })
            }
            offlineStack = null
          },
          trigger: function() {
            var fn,
              args,
              _self = this
            _unshift.call(arguments, cache)
            args = arguments
            fn = function() {
              return _trigger.apply(_self, args)
            }
            if (offlineStack) {
              return offlineStack.push(fn)
            }
            return fn()
          }
        }
      return ret
    }
    return {
      create: _create,
      listen: function(key, fn, last) {
        var event = this.create()
        event.listen(key, fn, last)
      },
      trigger: function() {
        var event = this.create()
        event.trigger.apply(this, arguments)
      }
    }
  })()
  return Event
})()

解析

1. 发布事件

// 发布事件
Event.trigger('click', 1)

/*
 1. 调用内部的 ret._trigger 方法
 2. 但此时offlineStack的离线消息栈中为[],所以走下面的代码,把缓存 trigger的 fn方法。
*/
var cache = _shift.call(arguments),
  key = _shift.call(arguments),
  args = arguments,
  _self = this,
  ret,
  stack = cache[key]
fn = function() {
  return _trigger.apply(_self, args)
}
if (offlineStack) {
  return offlineStack.push(fn)
}

2. 订阅事件

// 订阅事件
Event.listen('click', function(a) {
  console.log(a) // 输出: 1
})

/*
 * 1. 调用内部的 ret._listen方法,将cache里面缓存订阅事件的 key-fn。例如'click'和 'function()'
 */
_listen = function(key, fn, cache) {
  if (!cache[key]) {
    cache[key] = []
  }
  cache[key].push(fn)
}

/**
 * 2. 继续走ret.listen方法,将offlineStack遍历(each方法),取出离线消息,然后执行。
 */
listen: function(key, fn, last) {
  _listen(key, fn, cache)
  if (offlineStack === null) {
    return
  }
  if (last === 'last') {
    offlineStack.length && offlineStack.pop()()
  } else {
    each(offlineStack, function() {
      this()  // 执行fn()
    })
  }
  offlineStack = null
}

/**
 * 3. 由于在ret.trriger中 fn其实是个闭包,引用着args变量。所以根据闭包的特性,当cache对象的属性改变时,trriger中的arguments也跟着改变。
 */
trigger: function() {
  var fn,
    args,
    _self = this
  _unshift.call(arguments, cache)
  args = arguments
  fn = function() {
    console.log(args)
    // 此时这里的args从 发布阶段的 { '0': {}, '1': 'click', '2': 1 }
    // 变为 { '0': { click: [ [Function] ] }, '1': 'click', '2': 1 }
    return _trigger.apply(_self, args)
  }
  if (offlineStack) {
    return offlineStack.push(fn)
  }
  return fn()
}

/**
 * 4. 最后就简单了,就是执行订阅事件里面的function。在_trigger函数中。
 * 遍历stack
 */

_trigger = function() {
  var cache = _shift.call(arguments),
    key = _shift.call(arguments),
    args = arguments,
    _self = this,
    ret,
    stack = cache[key]
  if (!stack || !stack.length) {
    return
  }
  return each(stack, function() {
    return this.apply(_self, args)
  })
}

总结

  1. 看了此书这章节收益颇多,发布早于订阅,可以实现类似于 qq的离线消息
  2. 其实这部分的代码重点是一个闭包的运用,也正好对闭包进行一个实践吧。

你可能感兴趣的:(基于 javascript 设计模式一书中发布订阅模式的个人理解)