[附加题] 请实现下面的自定义事件 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。