首页 > 代码库 > 观察者模式(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)--自定义事件