[附加题] 请实现下面的自定义事件 Event 对象的接口,功能见注释(测试1)
该 Event 对象的接口需要能被其他对象拓展复用(测试2)
// 测试1
Event.on('test', function (result) {
    console.log(result);
});
Event.on('test', function () {
    console.log('test');
});
Event.emit('test', 'hello world'); // 输出 'hello world' 和 'test'
// 测试2
var 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] instanceof Array){
      var handlers=this.handlers[event];
      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。