# Lesson-9
---
關于事件部分,我思考了很久,也參考了許多,到底如何能用一個很簡單的方法實現一模一樣的on、off呢?
最后我的設計思路是:
> 1.有一個全局存儲所有`Events`的數組,存放每個`dom`元素上的事件。
> 2.給每個`DOM`一個`guid`的唯一標識符,通過這個`guid`來找出`Events`數組里的事件。
由于邏輯比較復雜,我們先來畫個圖看看。

首先,我們利用DOM可以增加自定義屬性的原理,在它的身上存一個guid。
之后整個事件機制就根據這個guid來進行查找與存儲。
接下來是代碼部分
```javascript
Kodo.Events = []; //事件綁定存放的事件
Kodo.guid = 0; //事件綁定的唯一標識
on: function(type, selector, fn) {
if (typeof selector == 'function') {
fn = selector; //兩個參數的情況
for (var i = 0; i < this.length; i++) {
if (!this[i].guid) {
this[i].guid = ++Kodo.guid;
//guid 不存在,給當前dom一個guid
Kodo.Events[Kodo.guid] = {};
/*
*給Events[guid] 開辟一個新對象
*用于存儲這個dom上的所有事件方法
*/
Kodo.Events[Kodo.guid][type] = [fn]; //每個方法都是一個數組
//給這個新對象,賦予事件數組 "click" : [fn1,fn2,...]
bind(this[i], type, this[i].guid);//綁定事件
} else {//guid存在的情況
var id = this[i].guid;
if (Kodo.Events[id][type]) {
//如果這存在是當前事件已經存過,不用在綁定事件,直接放入方法數組即可
Kodo.Events[id][type].push(fn);
} else {
//這是存新事件,所以需要重新綁定一次
Kodo.Events[id][type] = [fn];
bind(this[i], type, id);
}
}
}
}
}
function bind(dom, type, guid) {
dom.addEventListener(type, function(e) { //綁定相應事件
for (var i = 0; i < Kodo.Events[guid][type].length; i++) {
//循環執行那個方法數組即可
Kodo.Events[guid][type][i].call(dom, e); //正確的dom回調
}
}, false);
}
```
由于方法過長,我就把講解的都寫在了代碼里,這樣看的也會更方便一些。
代碼還是不夠形象!我們來看看log就能更清晰明白其中的奧秘。
通過控制臺log出`f.Events` 發現正是我們想要的結果,每個`dom`對應一個自己的`evtObj`, 通過`Kodo.Events[guid]` 可以得到指定的`evtObj`。然后即可取出自己相應的事件。

如果我繼續新增事件

可以發現,我只針對于第一個li增加了事件。log出Evnets也就只有第一個Object有新增,并且會增加到對應的事件數組里。
理解了這個后要解除事件綁定,那就非常簡單了。同樣根據guid查找到對應的方法數組,delete即可
```javascript
off: function(type, selector) {
if (arguments.length == 0) {
//如果沒傳參數,清空所有事件
for (var i = 0; i < this.length; i++) {
var id = this[i].guid;
for (var j in Kodo.Events[id]) {
delete Kodo.Events[id][j];
}
}
} else if (arguments.length == 1) {
//指定一個參數,則清空對應type的事件
for (var i = 0; i < this.length; i++) {
var id = this[i].guid;
delete Kodo.Events[id][type];
}
}
}
```
一個沒有帶有事件委托的on、off就可以這樣實現了。
那如果我們要實現帶委托的怎么辦呢?
我們可以用這同樣的思路實現,只是要多進行一個指定selector的存儲。
這個我們就放在下一課最后講解。
> star是尊重作者知識果實最好的回報 :)