# 设计模式:发布订阅模式

# 什么是发布订阅模式

定义:当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。

发布订阅模式又叫观察者模式,这应该是前端开发中应用最广泛的设计模式了。比如 addEventListener 就是建立监听关系,Element.click() 是改变自身状态,触发事件监听。

document.body.addEventListener( 'click', function(){
 alert(2);
}, false );
document.body.click(); // 模拟用户点击

# Coding

面向对象写法


function EventListener() {
  this.clientList = {}
}

EventListener.prototype.listen = function(key, fn) {
  if (!this.clientList[key]) {
      this.clientList[key] = [];
    }
  this.clientList[key].push(fn);
  return true;
}

EventListener.prototype.trigger = function() {
  const key = Array.prototype.shift.apply(arguments),
  fns = this.clientList[key];
  if (!fns || fns.length === 0) {
    return false;
  }
  for (let fn of fns) {
    fn.apply(null, arguments);
  }
  return true;
}

EventListener.prototype.remove = function(key, fn) {
  let fns = this.clientList[key];
  // 没有传入 fn 清空所有订阅的事件
  if (!fn) {
    this.clientList[key] = []
  }
  // 找到 fn 并移出
  fns.forEach((fnItem, index) => {
    if (fnItem === fn) {
      fns.splice(index, 1)
    }
  })
  return true;
}

let fn2 = function() {
  console.log(2)
}
let fn1 = function() {
  console.log(1)
}

var e = new EventListener();
e.listen('click', fn1);
e.listen('click', fn2);

e.remove('click', fn2);
e.trigger('click');

es6 写法

class EventListener {
  constructor() {
    this.clientList = {}
  }
  listen(key, fn) {
    if (!this.clientList[key]) {
      this.clientList[key] = [];
    }
    this.clientList[key].push(fn);
    return true;
  }
  trigger() {
    const key = Array.prototype.shift.apply(arguments),
    fns = this.clientList[key];
    if (!fns || fns.length === 0) {
      return false;
    }
    for (let fn of fns) {
      fn.apply(null, arguments);
    }
    return true;
  }
  remove(key, fn) {
    let fns = this.clientList[key];
    // 没有传入 fn 清空所有订阅的事件
    if (!fn) {
      this.clientList[key] = []
    }
    // 找到 fn 并移出
    fns.forEach((fnItem, index) => {
      if (fnItem === fn) {
        fns.splice(index, 1)
      }
    })
    return true;
  }
}

let fn2 = function() {
  console.log(2)
}
let fn1 = function() {
  console.log(1)
}

var e = new EventListener();
e.listen('click', fn1);
e.listen('click', fn2);

e.remove('click', fn2);
e.trigger('click');

# 总结

发布订阅模式的有点非常明显:

  1. 时间解耦
  2. 对象间解耦 在异步编程中有大量的适应场景。

但缺点是必须要合理的使用订阅模式。滥用的话会导致对象间的关系混乱,代码调试成本高。

上次更新: 9/9/2020, 7:25:14 PM