# 自定义事件那些事儿
TIP
TODO 重构
# DOM 元素的自定义事件
对于浏览器中的 DOM 元素来说,事件的实现非常简单,我们只需要调用浏览器 API 提供的 Event 接口就可以了。
语法
event = new Event(typeArg, eventInit);
示例
// 创建一个支持冒泡且不能被取消的look事件
var ev = new Event("look", { bubbles: true, cancelable: false });
document.dispatchEvent(ev);
// 事件可以在任何元素触发,不仅仅是document
myDiv.dispatchEvent(ev);
# 实现一个自定义事件的类
function MyCustomEvent() {
this._listener = {};
this.addEvent = (type, fn) => {
console.log("addEvent", type);
if (!this._listener[type]) {
this._listener[type] = [];
}
this._listener[type].push(fn);
};
this.attachEvent = (type, data) => {
console.log("attachEvent", type);
if (this._listener[type] && this._listener[type] instanceof Array) {
if (this._listener[type].length) {
this._listener[type].forEach((callback) => {
callback(data);
});
} else {
console.log(type, "event is not register");
}
}
};
this.removeEvent = (type, point) => {
console.log("removeEvent", type);
if (this._listener[type] && this._listener[type] instanceof Array) {
let index = this._listener[type].findIndex((callback) => {
return point === callback;
});
if (index > -1) {
this._listener[type].splice(index, 1);
point = null;
}
}
};
}
let eventTarget = new MyCustomEvent({});
function hello(e) {
console.log(e);
}
eventTarget.addEvent("hello", hello);
eventTarget.attachEvent("hello", "Helo This is CustomEvent");
// addEvent hello
// attachEvent hello
// Helo This is CustomEvent
eventTarget.removeEvent("hello", hello);
eventTarget.attachEvent("hello", "Helo This is CustomEvent");
// removeEvent hello
// attachEvent hello
// hello event is not register
TIP
我们这里实现的事件,其实就是调用注册在事件函数栈中的回调函数
TIP
这里没有实现 once,也就是一次性事件
# 包装一个对象使其具有事件
function proxyCustomEvent(_eventObj) {
_eventObj._listener = {};
_eventObj.addEvent = (type, fn) => {
if (!_eventObj._listener[type]) {
_eventObj._listener[type] = [];
}
_eventObj._listener[type].push(fn);
};
_eventObj.attachEvent = (type, data) => {
if (_eventObj._listener[type] && _eventObj._listener[type] instanceof Array) {
if (_eventObj._listener[type].length) {
_eventObj._listener[type].forEach((callback) => {
callback(data);
});
} else {
console.log(type, "event is not register");
}
}
};
_eventObj.removeEvent = (type, point) => {
if (_eventObj._listener[type] && _eventObj._listener[type] instanceof Array) {
let index = _eventObj._listener[type].findIndex((callback) => {
return point === callback;
});
if (index > -1) {
_eventObj._listener[type].splice(index, 1);
point = null;
}
}
};
return _eventObj;
}
示例
let obj = {};
obj = proxyCustomEvent(obj);
function test(e) {
console.log("test", e);
}
obj.addEvent("test", test);
obj.attachEvent("test", "proxyEvent");
// test proxyEvent
obj.removeEvent("test", test);
obj.attachEvent("test", "proxyEvent");
// test event is not register
# 模块化导出自定义事件类
function E() {}
E.prototype = {
on: function (name, callback, ctx) {
var e = this.e || (this.e = {});
(e[name] || (e[name] = [])).push({
fn: callback,
ctx: ctx
});
return this;
},
once: function (name, callback, ctx) {
var self = this;
function listener() {
self.off(name, listener);
callback.apply(ctx, arguments);
}
listener._ = callback;
return this.on(name, listener, ctx);
},
emit: function (name) {
var data = [].slice.call(arguments, 1);
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
var i = 0;
var len = evtArr.length;
for (i; i < len; i++) {
evtArr[i].fn.apply(evtArr[i].ctx, data);
}
return this;
},
off: function (name, callback) {
var e = this.e || (this.e = {});
var evts = e[name];
var liveEvents = [];
if (evts && callback) {
for (var i = 0, len = evts.length; i < len; i++) {
if (evts[i].fn !== callback && evts[i].fn._ !== callback) liveEvents.push(evts[i]);
}
}
liveEvents.length ? (e[name] = liveEvents) : delete e[name];
return this;
}
};
module.exports = E;
module.exports.TinyEmitter = E;
使用时让我们的类继承这个类即可
TIP
注意,由于这里我们使用的是 JS 触发的事件,因此我们的事件绑定函数是同步执行的。
# tiny-emitter
# mitt
# 学习资料
Event (opens new window)
EventTarget (opens new window)
Github - tiny-emitter (opens new window)
Github - mitt (opens new window)