首页 > 代码库 > 观察者模式(2)--自定义事件

观察者模式(2)--自定义事件

[附加题] 请实现下面的自定义事件 Event 对象的接口,功能见注释(测试1)该 Event 对象的接口需要能被其他对象拓展复用(测试2)// 测试1Event.on(‘test‘, function (result) {    console.log(result);});Event.on(‘test‘, function () {    console.log(‘test‘);});Event.emit(‘test‘, ‘hello world‘); // 输出 ‘hello world‘ 和 ‘test‘// 测试2var person1 = {};var person2 = {};Object.assign(person1, Event);Object.assign(person2, Event);person1.on(‘call1‘, function () {    console.log(‘person1‘);});person2.on(‘call2‘, function () {    console.log(‘person2‘);});person1.emit(‘call1‘); // 输出 ‘person1‘person1.emit(‘call2‘); // 没有输出person2.emit(‘call1‘); // 没有输出person2.emit(‘call2‘); // 输出 ‘person2‘<br>var Event = {    // 通过on接口监听事件eventName    // 如果事件eventName被触发,则执行callback回调函数    on: function (eventName, callback) {        //你的代码    },    // 触发事件 eventName    emit: function (eventName) {        //你的代码    }};

这是一道笔试题,考察的就是观察者模式,包括事件的定义和执行。这里看一下用JS原型写的观察者模式基本试例。

function Observer(){
  this.handlers={};
}
Observer.prototype={
  constructor:Observer,
  addHandler:function(type,handler){
    if(!this.handlers[type]){
      this.handlers[type]=[];
    }
    this.handlers[type].push(handler);
  },
  emit:function(event){
    if(!event.target){
      event.target=this;
    }
    if(this.handlers[event.type] instanceof Array){
      var handlers=this.handlers[event.type];
      for(var i=0;i<handlers.length;i++){
        handlers[i]();
      }
    }
  },
  removeHandlers:function(type,handler){
    if(this.handlers[type] instanceof Array){
      var handlers=this.handlers[type];
      for(var i=0;i<handlers.length;i++){
        if(handlers[i]===handler){
          break;
        }
      }
      handlers.splice(i,1);
    }
  }  
}

题目中的测试一,在事件触发时(emit),如果有参数,需要接收参数,如果没有参数,则直接忽略传参。

我们可以这样写:

var Event={
  on:function(event,callback){
    if(!this.handlers){
      this.handlers={};
    }
    if(!this.handlers[event]){
      this.handlers[type]=[];
    }
    this.handlers[type].push(event);

  },
  emit:funciton(event){
    var handlers=this.handlers[event];
    if(handlers){
      for(var i=0;i<handlers.length;i++){
        handlers[i](arguments[1]); //执行时的第二个参数,为传函数的参数
      }
    }
  }

}

  测试二的意思简单来说就是,两个不同的对象的自定义事件之间相互独立。

  在题目中提到了一个ES6的函数:Object.assign(target,source);作用是将源对象(source)中的所有可枚举属性复制到目标对象(target)中。在这个题中的意思就是,将Event中的可枚举对象放到person中。

  由于复制的是一个对象,而对象为引用类型,所以用Object.assign(target,source) 操作之后,复制的其实只是对象的引用。因此,person1和person2之间不独立。这显然不符合要求。那我们应该怎么做呢?既然Object.assign(target,source);复制的是可枚举的属性,那我们只要在定义handlers,将其定义为不可枚举属性即可,然后在person调用on方法时分别产生各自的handlers对象。

那新的代码应该这样:

var Event={  on:function(event,callback){    if(!this.handlers){      Object.defineProperty(this,‘handlers‘,{              value:{},
        enumberable:false, //不可枚举
         configurable:true,
         writable:true
});    }    if(!this.handlers[event]){      this.handlers[type]=[];    }    this.handlers[type].push(event);  },  emit:funciton(event){    var handlers=this.handlers[event];    if(handlers){      for(var i=0;i<handlers.length;i++){        handlers[i](arguments[1]); //执行时的第二个参数,为传函数的参数      }    }  }}

这里用到了Object.defineProperty(),表示将属性添加到对象,或修改现有属性的特性。

先看一个简单的例子:

  var a= {}

  Object.defineProperty(a,"b",{ value:123 })

  console.log(a.b);//123     它接受三个参数,而且都是必填的。。

传入参数

第一个参数:目标对象

第二个参数:需要定义的属性或方法的名字。

第三个参数:目标属性所拥有的特性。(descriptor)。

descriptor

有以下取值:

value:属性的值。

writable:如果为false,属性的值就不能被重写,只能为只读了,默认值为:true。

configurable:总开关,一旦为false,就不能再设置他的(value,writable,configurable)

enumberable:是否能在for...in循环中遍历出来或在Object.keys中列举出来,默认值为true。

这里我们不想属性可枚举,只需要在定义的时候将enumberable设置为false。

 

观察者模式(2)--自定义事件