一、js中观察者模式的使用

js是一门事件驱动的语言,所以在其语言内部存在大量的观察者模式的使用案例。
例如各种dom事件的监听函数等。其与观察者模式配合使用也是最完美的。

二、代码示例

下面我们定义一个供继承使用的Events功能模块,如下所示:

function Events() {}

Events.prototype = {
  events: {},

  emit: function(name, ...args) {

    if(!name || typeof name !== 'string') {
      throw new Error('Emit event name is null or not string .');
    }

    this.events[name] = this.events[name] || {};

    this.events[name]['args'] = args; //Array.from(arguments).slice(1);

    this.events[name]['callback'] = this.events[name]['callback'] || [];

    var i,
        callbackLen = this.events[name]['callback'].length;
    for(i = 0; i < callbackLen; ++ i) {
      this.events[name]['callback'][i](...this.events[name]['args']);
    }

  },
  on: function(name, callback) {

    if(!name || typeof name !== 'string') {
      throw new Error('On event name is null or not string .');
    }

    if(!callback || typeof callback !== 'function') {
      throw new Error('On event callback is null or not function .');
    }

    this.events[name] = this.events[name] || {};
    this.events[name]['callback'] = this.events[name]['callback'] || [];

    this.events[name]['callback'].push(callback);

    if('args' in this.events[name]) {
      var i,
          callbackLen = this.events[name]['callback'].length;
      for(i = 0; i < callbackLen; ++ i) {
        this.events[name]['callback'][i](...this.events[name]['args']);
      }
    }
  }
};

以上Events类的主要功能就是提供一个emit函数作为触发事件使用;on函数作为监听事件使用;events对象保存所有触发事件时发送的数据以及监听者监听事件后的回调函数数组,回调函数数组会在触发事件/监听事件时依次调用,并将触发事件时的数据传进去。

下面需要的是一个继承函数:

/**
* @param target: 目标类/子类
* @param source: 被继承类/基类/父类
*/
function inherits(target, source) {
  for(var key in source.prototype) {
    target.prototype[key] = source.prototype[key];
  }
}

下面定义一个供测试使用的类,并使其继承于我们写好的Events类:

// 定义一个 Obj 类
function Obj() {}

// 使 Obj 类继承于 Events 类
inherits(Obj, Events);

下面就开始我们的测试部分了:

var obj = new Obj();

obj.on('event1', function(arg1, arg2) {
  console.info('on event1 arg1=' + arg1 + ',arg2=' + JSON.stringify(arg2));
});

obj.emit('event1', 1, {a: 1});


obj.emit('event2', 2, {a: 2});

obj.on('event2', function(arg1, arg2) {
  console.info('on event2 arg1=' + arg1 + ',arg2=' + JSON.stringify(arg2));
});

以上我们创建了一个Obj对象obj,并且使之监听了event1和event2这两个事件,且在监听前后分别触发了这两个事件,最后让我们来看看以上代码的执行输出的结果如何:

on event1 arg1=1,arg2={"a":1}
on event2 arg1=2,arg2={"a":2}

以上输出结果已经说明了我们自定义的Events类起到了事件监听和事件触发的作用。需要说明的是事件的触发和监听只能在同一个对象上实现,不同对象之间的事件是不共享的,即使是同一类的不同对象也不可以。

三、该模式的优点

观察者模式的最大优点就是实现了模块之间的解耦。即只需要触发一个事件即可,不需要关心有没有观察者,有多少观察者。观察者也不需要关心谁触发了事件,在哪儿触发了事件,只需要监听这个事件并处理自身的逻辑即可。这使得触发事件的生产者和监听事件的消费者之间充分的松散了耦合关系。