亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

jQuery-1.9.1源碼分析系列(十)事件系統之事件體系結構

2019-11-20 11:14:09
字體:
來源:轉載
供稿:網友

又是一個重磅功能點。

  在分析源碼之前分析一下體系結構,有助于源碼理解。實際上在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,并調用相應的函數??梢园裩andleEvent稱為“主監聽函數” 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" id="chua" onClick="f0();">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。

  這個時候假設又新添了一個事件綁定:addEvent(dom,"focus",f3);那么element.events.focus[3] = f3;這也是對象相比于數組的方便之處,數組不可能沒有下標0,1,2就直接有3了,但是對象卻可以,此時3是作為對象的一個屬性名稱。

  這樣的設計,其實已經具備了jquery事件系統的雛形,包含了幾個最主要的特點:

  1)element上的所有事件,將保存到element.events屬性中,不是直接綁定到element上;這樣一個事件可以有無數個響應函數。

  2)handleEvent作為element所有事件的“主監聽函數”,有它統一管理element上的所有函數。

  3)所有瀏覽器都支持element["on" + type]事件綁定方式,跨瀏覽器兼容。

  好啦,明白了addEvent的事件結構,這個想法確實讓人覺得眼前一亮。下面分析jQuery的事件結構

b. jQuery的事件結構

  所有的函數添加事件都會進入jQuery.event.add函數。該函數有兩個主要功能:添加事件、附加很多事件相關信息。我們直接上源碼,源碼思想和Dean Edwards的跨瀏覽器兼容事件添加處理類似。

  源碼分析

add: function( elem, types, handler, data, selector ) { var tmp, events, t, handleObjIn,  special, eventHandle, handleObj,  handlers, type, namespaces, origType,  //獲取elem節點對應的緩存數據  elemData = jQuery._data( elem ); //沒有數據或文本/注釋節點不能附加事件(但是允許附加普通對象) if ( !elemData ) {  return; } //調用者能通過自定義數據替換handler if ( handler.handler ) {  handleObjIn = handler;  handler = handleObjIn.handler;  selector = handleObjIn.selector; } //確保handler函數有唯一的ID,后續會用來查找/刪除這個handler函數 if ( !handler.guid ) {  handler.guid = jQuery.guid++; } //如果是初次進入,初始化元素的事件結構和主事件響應入口 if ( !(events = elemData.events) ) {  events = elemData.events = {}; } if ( !(eventHandle = elemData.handle) ) {  eventHandle = elemData.handle = function( e ) {   //當一個事件被調用后頁面已經卸載,則放棄jQuery.event.trigger()的第二個事件,   return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?   jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :   undefined;  };  //將elem作為handle函數的一個特征防止ie非本地事件引起的內存泄露  eventHandle.elem = elem; } //多個事件使用空格隔開的處理  //如jQuery(...).bind("mouseover mouseout", fn);  //core_rnotwhite = //S+/g;匹配空白字符  types = ( types || "" ).match( core_rnotwhite ) || [""];  t = types.length;  while ( t-- ) {  //rtypenamespace = /^([^.]*)(?:/.(.+)|)$/;  //獲取命名空間和原型事件  tmp = rtypenamespace.exec( types[t] ) || [];  type = origType = tmp[1];  namespaces = ( tmp[2] || "" ).split( "." ).sort();  //如果事件改變其類型,使用special事件處理器來處理更改后的事件類型  special = jQuery.event.special[ type ] || {};  //如果選擇器已定義,確定special事件API類型,否則給他一個類型  type = ( selector ? special.delegateType : special.bindType ) || type;  //基于新設置的類型更新special  special = jQuery.event.special[ type ] || {};  // handleObj貫穿整個事件處理  handleObj = jQuery.extend({   type: type,   origType: origType,   data: data,   handler: handler,   guid: handler.guid,   selector: selector,   // For use in libraries implementing .is(). We use this for POS matching in `select`   //"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?://(" +   //whitespace + "*((?:-//d)?//d*)" + whitespace + "*//)|)(?=[^-]|$)", "i" )   //用來判斷親密關系   needsContext: selector && jQuery.expr.match.needsContext.test( selector ),   namespace: namespaces.join(".")  }, handleObjIn );  //初次使用時初始化事件處理器隊列  if ( !(handlers = events[ type ]) ) {   handlers = events[ type ] = [];   handlers.delegateCount = 0;   //非自定義事件,如果special事件處理器返回false,則只能使用addEventListener/attachEvent   if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {    //給元素綁定全局事件    if ( elem.addEventListener ) {     elem.addEventListener( type, eventHandle, false );    } else if ( elem.attachEvent ) {     elem.attachEvent( "on" + type, eventHandle );    }   }  }  //自定義事件綁定  if ( special.add ) {   special.add.call( elem, handleObj );   if ( !handleObj.handler.guid ) {    handleObj.handler.guid = handler.guid;   }  }  //將事件對象handleObj添加到元素的處理列表,代理計數遞增  if ( selector ) {   handlers.splice( handlers.delegateCount++, 0, handleObj );  } else {   handlers.push( handleObj );  }  //跟蹤那個事件曾經被使用過,用于事件優化  jQuery.event.global[ type ] = true; } //防止ie內存泄漏 elem = null;}

  依然用實例來說明jQuery的事件結構

<div id="#center"></div><script> function dohander(){console.log("dohander")}; function dot(){console.log("dot");} $(document).on("click",'#center',dohander) .on("click",'#center',dot) .on("click",dot);</script>

  經過添加處理環節,事件添加到了元素上,而且節點對應的緩存數據也添加了相應的數據。結構如下

elemData = jQuery._data( elem );elemData = {  events: {    click: {//Array[3]      0: {        data: undefined/{...},        guid: 2, //處理函數的id        handler: function dohander(){…},        namespace: "",        needsContext: false,        origType: "click",        selector: "#center",//選擇器,用來區分不同事件源        type: "click"      }      1: {        data: undefined/{...},        guid: 3,        handler: function dot(){…},        namespace: "",        needsContext: false,        origType: "click",        selector: "#center",        type: "click"      }      2: {        data: undefined,        guid: 3,        handler: function dot(){…},        namespace: "",        needsContext: false,        origType: "click",        selector: undefined,        type: "click"      }      delegateCount: 2,//委托事件數量,有selector的才是委托事件      length: 3    }  }  handle: function ( e ) {…}/*事件處理主入口*/{    elem: document//屬于handle對象的特征  }}

  jQuery的處理和Dean Edwards的跨瀏覽器兼容事件添加處理類似,比如為每一個函數添加guid;使用events對象存放響應事件列表,有一個總的事件處理入口handle等。

  jQuery做了哪些改進?

  1)事件數據不再直接保存在節點上,而是使用jQuery緩存系統內(內部使用的緩存jQuery._data方式存取)

  2)事件委托:綁定到當前節點(例子中當前節點是document根節點)的處理函數不僅僅包含當前節點觸發事件(click)響應時處理的事件(例子中selector為undefined時對應的處理函數dot);還代理了其他節點(例子中的#center節點)觸發事件(click)響應時處理的事件(例子中selector為"#center"對應的處理事件doHandler和dot);委托機制在后續分析。

  3)增加了很多功能數據,比如命名空間namespace:這個主要用在自定義事件自定義觸發,比如$(document).on("chua.click",'#center',dot),主動觸發$("#center").trigger("chua.click")。還有額外數據data:雖然沒有看到那個地方有被用到。

  到此jQuery的事件結構就清楚了。后面再分析事件的綁定和觸發以及委托原理。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕亚洲欧美| 91av视频在线观看| 国产成人黄色av| 欧美午夜无遮挡| 精品中文字幕在线观看| 97精品视频在线| 深夜福利日韩在线看| 精品二区三区线观看| 伦伦影院午夜日韩欧美限制| 国产在线98福利播放视频| 国产在线久久久| 中文字幕精品一区久久久久| 色婷婷久久av| 亚洲国产精品一区二区三区| 欧美国产日韩一区二区在线观看| 欧美激情高清视频| 在线免费看av不卡| 91国产精品电影| 91精品国产91久久久久久最新| 中文字幕精品www乱入免费视频| 久久精品免费电影| 日本久久91av| 69久久夜色精品国产7777| 久久久久久久香蕉网| 自拍亚洲一区欧美另类| 国内精品一区二区三区| 中文字幕亚洲色图| 欧美国产中文字幕| 亚洲精品久久视频| 不卡中文字幕av| 在线视频欧美日韩精品| 国产精品久久视频| 国外成人在线播放| 美女啪啪无遮挡免费久久网站| 国产精品久久97| 青草成人免费视频| 精品久久久999| 日韩欧美亚洲范冰冰与中字| 精品少妇一区二区30p| 精品无人国产偷自产在线| 国内精品国产三级国产在线专| 欧美性精品220| 中文字幕欧美日韩精品| 国产精品美女呻吟| 97超碰蝌蚪网人人做人人爽| 国内精品小视频| 成人久久一区二区三区| 亚洲国产精彩中文乱码av在线播放| 亚洲va男人天堂| 日本午夜在线亚洲.国产| 久久中文字幕一区| 亚洲资源在线看| 精品久久久国产精品999| 在线播放日韩专区| 亚洲人成在线观看| 日韩一区二区久久久| 亚洲欧美中文日韩在线v日本| 久久精品国产综合| 精品国产一区二区三区在线观看| 91在线视频成人| 色综合男人天堂| 亚洲精品suv精品一区二区| 久久影视免费观看| 日韩精品视频在线观看免费| 精品国产美女在线| 欧美日韩亚洲一区二| 国产精品午夜国产小视频| 国产亚洲激情视频在线| 最新国产成人av网站网址麻豆| 亚洲一级一级97网| 国产亚洲欧洲黄色| 成人在线视频福利| 亚洲第一区在线观看| 日韩精品视频在线观看网址| 成人午夜激情网| 亚洲bt天天射| 日韩精品久久久久久久玫瑰园| 日韩av免费观影| 黑人巨大精品欧美一区二区免费| 亚洲国产97在线精品一区| 国产一区二区在线免费视频| 国产欧美日韩中文字幕在线| 国产亚洲欧美aaaa| 亚洲精品电影网站| 人体精品一二三区| 欧美日韩一区二区免费在线观看| 欧美久久久精品| 欧美成人免费全部观看天天性色| 最近2019中文字幕mv免费看| 亚洲最大成人免费视频| 久久亚洲综合国产精品99麻豆精品福利| 7m精品福利视频导航| xxxxxxxxx欧美| 亚洲欧美在线第一页| 欧美一区二区三区四区在线| 欧美激情一区二区三区在线视频观看| 伊人亚洲福利一区二区三区| 国产精品一区二区三区在线播放| 国产不卡视频在线| 国产成人一区二区三区小说| 国产精品黄页免费高清在线观看| 欧美日韩亚洲系列| 欧美日韩国产限制| 国产精品视频网| 日韩精品视频免费| 亚洲人午夜色婷婷| 夜夜嗨av色综合久久久综合网| 亚洲欧美国产另类| 九九热这里只有在线精品视| 久久影院中文字幕| 538国产精品一区二区免费视频| 欧美成人在线免费| 视频直播国产精品| 亚洲精品永久免费| 日本韩国欧美精品大片卡二| 国产精品私拍pans大尺度在线| 欧美日韩免费一区| 久久久精品免费| 亚洲第一精品夜夜躁人人爽| 欧美中文在线观看| 成人免费看片视频| 91精品中国老女人| 97成人在线视频| 亚洲天堂成人在线视频| 国产亚洲人成网站在线观看| 欧美日韩在线免费观看| 亚洲一区二区三区乱码aⅴ蜜桃女| 日韩精品在线免费播放| 国产精品欧美一区二区| 视频直播国产精品| 亚洲一区第一页| 精品日韩中文字幕| 性视频1819p久久| 2019中文字幕在线免费观看| 国产亚洲精品美女| 性欧美视频videos6一9| 日韩中文字幕视频| 国产精品久久激情| 日日骚av一区| 川上优av一区二区线观看| 红桃视频成人在线观看| 91久久精品久久国产性色也91| 日韩国产高清污视频在线观看| 日韩毛片中文字幕| 亚洲一区二区免费在线| 国产精品国产三级国产aⅴ浪潮| 精品视频www| 日韩欧美亚洲一二三区| 81精品国产乱码久久久久久| 亚洲男人天堂视频| 欧美性高潮床叫视频| 欧美一级电影免费在线观看| 91高清视频免费| 国产99久久精品一区二区 夜夜躁日日躁| 欧美性jizz18性欧美| 欧美一区二三区| 亚洲a在线观看| 91精品视频在线播放| 国产成人精品日本亚洲| 久久久国产成人精品| 亚洲成人动漫在线播放| 成人xxxxx| 情事1991在线| 中文字幕精品—区二区|