1.9.1源码分析系列(十)事件系统之事件体系结构

又是一个重磅功能点。

  在分析源码之前分析一下体系结构,有助于源码理解。实际上在jQuery出现之前,Dean Edwards的跨浏览器AddEvent()设计做的已经比较优秀了;而且jQuery事件系统的设计思想也是基于该思想的,所以我们先分析一下Dean Edwards前辈的事件绑定。

a. jQuery事件原型——Dean Edwards的跨浏览器AddEvent()设计

  源码解读

//事件添加方法 function addEvent(element, type, handler) {   //保证每个不同的事件响应函数只有唯一一个id if (!handler.$$guid) handler.$$guid = addEvent.guid++;   // 给element维护一个events属性,初始化为一个空对象。 // element.events的结构类似于 { "click": {...}, "dbclick": {...}, "change": {...} } if (!element.events) element.events = {};   // 试图取出element.events中当前事件类型type对应的对象(这个对象更像数组),赋值给handlers   //如果element.events中没有当前事件类型type对应的对象则初始化 var handlers = element.events[type];   if (!handlers) {      handlers = element.events[type] = {};      // 如果这个element已经有了一个对应的事件的响应方法,例如已经有了onclick方法 // 就把element的onclick方法赋值给handlers的0元素,此时handlers的结构就是: // { 0: function(e){...} },这也是为什么addEvent.guid初始化为1的原因,预留看为0的空间; // 此时element.events的结构就是: { "click": { 0: function(e){...} }, /*省略其他事件类型*/ } if (element["on" + type]) { handlers[0] = element["on" + type]; } }   // 把当前的事件handler存放到handlers中,handler.$$guid = addEvent.guid++; addEvent.guid = 1; 肯定是从1开始累加的   //因此,这是handlers的结构可能就是 { 0: function(e){...}, 1: function(){}, 2: function(){} 等等... } handlers[handler.$$guid] = handler;   //下文定义了一个handleEvent(event)函数,将这个函数,绑定到element的type事件上作为事件入口。   //说明:在element进行click时,将会触发handleEvent函数,handleEvent函数将会查找element.events,并调用相应的函数。可以把handleEvent称为“主监听函数” element["on" + type] = handleEvent; }; //计数器 addEvent.guid = 1; function removeEvent(element, type, handler) { // delete the event handler from the hash table if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; } }; function handleEvent(event) {   //兼容ie   event = event || window.event;   //this是响应事件的节点,这个接点上有events属性(在addEvent中添加的)   //获取节点对应事件响应函数列表 var handlers = this.events[event.type]; // 循环响应函数列表执行   for (var i in handlers) { //保持正确的作用域,即this关键字      this.$$handleEvent = handlers[i]; this.$$handleEvent(event); } };

重新梳理一下数据结构,使用一个例子

<input type="text"> function f0(){...} function f1(){...} function f2(){...} function f3(){...} var dom = document.getElementById("chua"); addEvent(dom,"click",f1); addEvent(dom,"change",f1); addEvent(dom,"change",f2); addEvent(dom,"click",f3); addEvent(dom,"change",f3);

  经过addEvent()函数之后,当前的数据结构为:

element: {   onclick: handleEvent(event), //click事件的主监听函数   onchage: handleEvent(event),  //change事件的主监听函数   events: {     click:{//这是一个类数组       0: f0, //element已有的事件       1: f1,  //下标1实际上就是f1.$$guid       3: f3 //下标3实际上就是f3.$$guid,需要注意的是每一个响应事件都有一个唯一的$$guid作为下标       ...     },     change:{//这是一个类数组       1: f1,       2: f2,       3: f3     } } }

  事件系统会根据调用addEvent的顺序给每个响应函数(也就是addEvent(element, type, handler)中的第三个参数handler)打上标记$$guid。源码

  //保证每个不同的事件响应函数只有唯一一个id if (!handler.$$guid) handler.$$guid = addEvent.guid++;

  最终三个响应函数的$$guid标记分别是

  f1.$$guid = 1
  f2.$$guid = 2
  f3.$$guid = 3

  而根据源码中

handlers[handler.$$guid] = handler;

  那么某一个函数在任何事件响应函数集合中的下标位置是固定的。比如click和change事件都调用f3作为响应事件,那么f3在element.events.click以及element.events.change中的下标位置都是f3.$$guid = 3;即element.events.click[3] = element.events.change[3] = f3。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgzjzs.html