請實現下面的自定義事件 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'
var Event = {
// 經過on接口監聽事件eventName
// 若是事件eventName被觸發,則執行callback回調函數
on: function (eventName, callback) {
//你的代碼
},
// 觸發事件 eventName
emit: function (eventName) {
//你的代碼
}
};
複製代碼
差點沒把我看暈...前端
好吧,一步一步來看看怎麼回事。git
觀察者模式:github
這是一種建立鬆散耦合代碼的技術。它定義對象間 一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。由主體和觀察者組成,主體負責發佈事件,同時觀察者經過訂閱這些事件來觀察該主體。主體並不知道觀察者的任何事情,觀察者知道主體並能註冊事件的回調函數。ajax
例子:數組
假如咱們正在開發一個商城網站,網站裏有header頭部、nav導航、消息列表、購物車等模塊。這幾個模塊的渲染有一個共同的前提條件,就是必須先用ajax異步請求獲取用戶的登陸信息。這是很正常的,好比用戶的名字和頭像要顯示在header模塊裏,而這兩個字段都來自用戶登陸後返回的信息。這個時候,咱們就能夠把這幾個模塊的渲染事件都放到一個數組裏面,而後待登陸成功以後再遍歷這個數組而且調用每個方法。 基本模式:異步
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: 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, len=handlers.length; i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
複製代碼
大概意思就是,建立一個事件管理器。handles是一個存儲事件處理函數的對象。函數
addHandle
:是添加事件的方法,該方法接收兩個參數,一個是要添加的事件的類型,一個是這個事件的回調函數名。調用的時候會首先遍歷handles這個對象,看看這個類型的方法是否已經存在,若是已經存在則添加到該數組,若是不存在則先建立一個數組而後添加。學習
fire
:是執行handles這個對象裏面的某個類型的每個方法。測試
removeHandle
:是相應的刪除函數的方法。網站
好啦,回到題目,分析一下。
// 測試1
Event.on('test', function (result) {
console.log(result);
});
Event.on('test', function () {
console.log('test');
});
Event.emit('test', 'hello world'); // 輸出 'hello world' 和 'test'
複製代碼
意思就是,定義一個叫test
類型的事件集,而且註冊了兩個test
事件。而後調用test
事件集裏面的所有方法。在這裏on方法等價於addHandle方法,emit方法等價於fire方法。其中第一個參數就是事件類型,第二個參數就是要傳進函數的參數。
是否是這個回事呢?很好,那麼咱們要寫的代碼就是:
var Event = {
// 經過on接口監聽事件eventName
// 若是事件eventName被觸發,則執行callback回調函數
on: function (eventName, callback) {
//個人代碼
if(!this.handles){
this.handles={};
}
if(!this.handles[eventName]){
this.handles[eventName]=[];
}
this.handles[eventName].push(callback);
},
// 觸發事件 eventName
emit: function (eventName) {
//你的代碼
if(this.handles[arguments[0]]){
for(var i=0;i<this.handles[arguments[0]].length;i++){
this.handles[arguments[0]][i](arguments[1]);
}
}
}
};
複製代碼
這樣測試,完美地經過了測試一。
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'
複製代碼
大概意思就是爲兩個不一樣person註冊自定義事件,而且兩個person之間是互相獨立的。
直接測試,發現輸出了
這個好像是題目要求有點出入呢,或者這纔是題目的坑吧!
解釋一下,Object.assign(person1, Event)
;
這個是ES6的新對象方法,用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。
意思是將Event裏面的可枚舉的對象和方法放到person1裏面。
也就是說,若是源對象某個屬性的值是對象,那麼目標對象拷貝獲得的是這個對象的引用。因爲進行測試一的時候調用了on方法,因此event裏面已經有了handles這個可枚舉的屬性。而後再分別合併到兩個person裏面的話,兩個person對象裏面的handles都只是一個引用。因此就互相影響了。
若是assign方法要實現深克隆則要這樣:
問題是,題目已經固定了方式,咱們不能修改這個方法。
因此,咱們必須將handles這個屬性定義爲不可枚舉的,而後在person調用on方法的時候再分別產生handles這個對象。
也就是說正確的作法應該是:
var Event = {
// 經過on接口監聽事件eventName
// 若是事件eventName被觸發,則執行callback回調函數
on: function (eventName, callback) {
//你的代碼
if(!this.handles){
//this.handles={};
Object.defineProperty(this, "handles", {
value: {},
enumerable: false,
configurable: true,
writable: true
})
}
if(!this.handles[eventName]){
this.handles[eventName]=[];
}
this.handles[eventName].push(callback);
},
// 觸發事件 eventName
emit: function (eventName) {
//你的代碼
if(this.handles[arguments[0]]){
for(var i=0;i<this.handles[arguments[0]].length;i++){
this.handles[arguments[0]][i](arguments[1]);
}
}
}
};
複製代碼
經過這道題,感受考得真的很巧妙並且很考基礎。