我认为,事件是我们前端最为熟悉的编程模式,在前端开发中能接触太多太多,而且相对而言,事件是一种相对容易理解,逻辑性高的的模式,对于优化组件/插件的逻辑是一个很好的应用模式。
这文章主要是用JavaScript实现3级dom事件机制,后面的更新会涉及应用倒组件开发当中。
jQuery 是实现了一个独立的事件方法,甚至可以在自定义对象(object)上绑定事件,功能十分强大。
例:
//import jQuery
$(selector).on('yourEvent',function(){
console.log('i m yourEvent handler');
})
$(selector).trigger('yourEvent')
上面例子就是jQuery 的自定义事件实现方式,下面再展示一个绑定对象上的事件,它其实是一个订阅/发布模式的实现 —> cowboy 实现的简易订阅/发布
例:
(function($) {
var o = $({});
$.subscribe = function() {
o.on.apply(o, arguments);
};
$.unsubscribe = function() {
o.off.apply(o, arguments);
};
$.publish = function() {
o.trigger.apply(o, arguments);
};
}(jQuery));
jQuery的事件实现原理这里不作陈述,这里提供Aaron的jQuery源码解析传送门
在原生JavaScript对象,自定义的object 对象并没有事件的概念, 而实际上 我们希望某些组件拥有事件驱动,js拥有完整的事件驱动对象,但是我们开发者没有办法使用(第二章有提及),不过我们还是能基于官方的文档事件一个与js一样的事件对象。
我们从官方的事件文档中看到一个简单的事件驱动对象实现例子。
var eventTarget = function() {
this.listeners = {};
};
eventTarget.prototype.listeners = null;
eventTarget.prototype.addEventListener = function(type, callback) {
if(!(type in this.listeners)) {
this.listeners[type] = [];
}
this.listeners[type].push(callback);
};
eventTarget.prototype.removeEventListener = function(type, callback) {
if(!(type in this.listeners)) {
return;
}
var stack = this.listeners[type];
for(var i = 0, l = stack.length; i < l; i++) {
if(stack[i] === callback){
stack.splice(i, 1);
return this.removeEventListener(type, callback);
}
}
};
eventTarget.prototype.dispatchEvent = function(event) {
if(!(event.type in this.listeners)) {
return;
}
var stack = this.listeners[event.type];
event.target = this;
for(var i = 0, l = stack.length; i < l; i++) {
stack[i].call(this, event);
}
};
这个例子可以是我们实现自己事件的最顶层类。
假设我们已经有了 eventTarget对象,下面我们要做的就是把事件绑定到对象上
var component = function(){};
component.prototype = new eventTarget();
var com_1 = new component();
com_1.addEventListener('click',function(){
console.log('i m listenning click event')
});
com_1.dispatchEvent(new MouseEvent('click')) //i m listenning click event
htmlElement的事件流是独立的,实际上的C++在做事件流的传递,自定义的事件没法加入到这个事件流当中。
这种冒泡的事件流机制非常常见,在ios,Android中的事件驱动都是类似这种机制。在事件中这样的思想/机制能把事件的耦合度降低。在大型组件的事件逻辑中,加入这种机制个人认为是非常好的。
var flowEvent = function (parentNode) {
this.listeners = {};
this.childrenNode = [];
this.parentNode = parentNode; // 父节点
parentNode && parentNode.childrenNode.push(this) //加入父节点的子节点
}
flowEvent.prototype = new eventTarget(); //继承事件
flowEvent.prototype.dispatchEvent = function (event) { //重写事件分发方法
if(!(event.type in this.listeners)) {
return;
}
var stack = this.listeners[event.type]; //要独立lisenter
event.target = this;
for(var i = 0, l = stack.length; i < l; i++) {
stack[i].call(this, event);
}
if(event.target){
//todo 加入阻止同类事件&阻止传递
this.childrenNode.length>0 && this.childrenNode.forEach(function (v) {
v.dispatchEvent(event)
})
}else{
this.parentNode && this.parentNode.dispatchEvent(event)
}
}