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

首頁 > 語言 > JavaScript > 正文

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

2024-05-06 16:25:12
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了jQuery-1.9.1源碼分析系列(十)事件系統之事件體系結構的相關資料,需要的朋友可以參考下

又是一個重磅功能點。

在分析源碼之前分析一下體系結構,有助于源碼理解。實際上在jQuery出現之前,Dean Edwards的跨瀏覽器AddEvent()設計做的已經比較優秀了;而且jQuery事件系統的設計思想也是基于該思想的,所以我們先分析一下Dean Edwards前輩的事件綁定。

a. jQuery事件原型——Dean Edwards的跨瀏覽器AddEvent()設計

源碼解讀

 

 
  1. //事件添加方法 
  2. function addEvent(element, type, handler) { 
  3.   //保證每個不同的事件響應函數只有唯一一個id 
  4. if (!handler.$guid) handler.$guid = addEvent.guid++; 
  5.   // 給element維護一個events屬性,初始化為一個空對象。  
  6. // element.events的結構類似于 { "click": {...}, "dbclick": {...}, "change": {...} }  
  7. if (!element.events) element.events = {}; 
  8.   // 試圖取出element.events中當前事件類型type對應的對象(這個對象更像數組),賦值給handlers 
  9.   //如果element.events中沒有當前事件類型type對應的對象則初始化 
  10. var handlers = element.events[type]; 
  11.   if (!handlers) { 
  12.      handlers = element.events[type] = {}; 
  13.      // 如果這個element已經有了一個對應的事件的響應方法,例如已經有了onclick方法 
  14. // 就把element的onclick方法賦值給handlers的0元素,此時handlers的結構就是: 
  15. // { 0: function(e){...} },這也是為什么addEvent.guid初始化為1的原因,預留看為0的空間; 
  16. // 此時element.events的結構就是: { "click": { 0: function(e){...} }, /*省略其他事件類型*/ }  
  17. if (element["on" + type]) { 
  18. handlers[0] = element["on" + type]; 
  19.   // 把當前的事件handler存放到handlers中,handler.$guid = addEvent.guid++; addEvent.guid = 1; 肯定是從1開始累加的  
  20.   //因此,這是handlers的結構可能就是 { 0: function(e){...}, 1: function(){}, 2: function(){} 等等... } 
  21. handlers[handler.$guid] = handler; 
  22.   //下文定義了一個handleEvent(event)函數,將這個函數,綁定到element的type事件上作為事件入口。 
  23.   //說明:在element進行click時,將會觸發handleEvent函數,handleEvent函數將會查找element.events,并調用相應的函數。可以把handleEvent稱為“主監聽函數” 
  24. element["on" + type] = handleEvent; 
  25. }; 
  26. //計數器 
  27. addEvent.guid = 1; 
  28. function removeEvent(element, type, handler) { 
  29. // delete the event handler from the hash table 
  30. if (element.events && element.events[type]) { 
  31. delete element.events[type][handler.$guid]; 
  32. }; 
  33. function handleEvent(event) { 
  34.   //兼容ie 
  35.   event = event || window.event; 
  36.   //this是響應事件的節點,這個接點上有events屬性(在addEvent中添加的) 
  37.   //獲取節點對應事件響應函數列表 
  38. var handlers = this.events[event.type]; 
  39. // 循環響應函數列表執行 
  40.   for (var i in handlers) { 
  41. //保持正確的作用域,即this關鍵字 
  42.      this.$handleEvent = handlers[i]; 
  43. this.$handleEvent(event); 
  44. }; 

重新梳理一下數據結構,使用一個例子

 

 
  1. <input type="text" id="chua" onClick="f0();"
  2. function f0(){...} 
  3. function f1(){...} 
  4. function f2(){...} 
  5. function f3(){...} 
  6. var dom = document.getElementById("chua"); 
  7. addEvent(dom,"click",f1); 
  8. addEvent(dom,"change",f1); 
  9. addEvent(dom,"change",f2); 
  10. addEvent(dom,"click",f3); 
  11. addEvent(dom,"change",f3); 

經過addEvent()函數之后,當前的數據結構為:

 

 
  1. element: { 
  2.   onclick: handleEvent(event), //click事件的主監聽函數 
  3.   onchage: handleEvent(event),  //change事件的主監聽函數 
  4.   events: { 
  5.     click:{//這是一個類數組 
  6.       0: f0, //element已有的事件 
  7.       1: f1,  //下標1實際上就是f1.$guid 
  8.       3: f3 //下標3實際上就是f3.$guid,需要注意的是每一個響應事件都有一個唯一的$guid作為下標  
  9.       ... 
  10.     }, 
  11.     change:{//這是一個類數組 
  12.       1: f1, 
  13.       2: f2, 
  14.       3: f3 
  15.     } 

事件系統會根據調用addEvent的順序給每個響應函數(也就是addEvent(element, type, handler)中的第三個參數handler)打上標記$$guid。源碼

 

 
  1. //保證每個不同的事件響應函數只有唯一一個id 
  2. f (!handler.$guid) handler.$guid = addEvent.guid++; 

最終三個響應函數的$$guid標記分別是

f1.$$guid = 1

f2.$$guid = 2

f3.$$guid = 3

而根據源碼中

 

 
  1. 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的跨瀏覽器兼容事件添加處理類似。

源碼分析

 

 
  1. add: function( elem, types, handler, data, selector ) { 
  2. var tmp, events, t, handleObjIn, 
  3. special, eventHandle, handleObj, 
  4. handlers, type, namespaces, origType, 
  5. //獲取elem節點對應的緩存數據 
  6. elemData = jQuery._data( elem ); 
  7. //沒有數據或文本/注釋節點不能附加事件(但是允許附加普通對象) 
  8. if ( !elemData ) { 
  9. return
  10. //調用者能通過自定義數據替換handler 
  11. if ( handler.handler ) { 
  12. handleObjIn = handler; 
  13. handler = handleObjIn.handler; 
  14. selector = handleObjIn.selector; 
  15. //確保handler函數有唯一的ID,后續會用來查找/刪除這個handler函數 
  16. if ( !handler.guid ) { 
  17. handler.guid = jQuery.guid++; 
  18. //如果是初次進入,初始化元素的事件結構和主事件響應入口 
  19. if ( !(events = elemData.events) ) { 
  20. events = elemData.events = {}; 
  21. if ( !(eventHandle = elemData.handle) ) { 
  22. eventHandle = elemData.handle = function( e ) { 
  23. //當一個事件被調用后頁面已經卸載,則放棄jQuery.event.trigger()的第二個事件, 
  24. return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? 
  25. jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : 
  26. undefined; 
  27. }; 
  28. //將elem作為handle函數的一個特征防止ie非本地事件引起的內存泄露 
  29. eventHandle.elem = elem; 
  30. //多個事件使用空格隔開的處理 
  31.   //如jQuery(...).bind("mouseover mouseout", fn); 
  32.   //core_rnotwhite = //S+/g;匹配空白字符 
  33.   types = ( types || "" ).match( core_rnotwhite ) || [""]; 
  34.   t = types.length; 
  35.   while ( t-- ) { 
  36. //rtypenamespace = /^([^.]*)(?:/.(.+)|)$/; 
  37. //獲取命名空間和原型事件 
  38. tmp = rtypenamespace.exec( types[t] ) || []; 
  39. type = origType = tmp[1]; 
  40. namespaces = ( tmp[2] || "" ).split( "." ).sort(); 
  41. //如果事件改變其類型,使用special事件處理器來處理更改后的事件類型 
  42. special = jQuery.event.special[ type ] || {}; 
  43. //如果選擇器已定義,確定special事件API類型,否則給他一個類型 
  44. type = ( selector ? special.delegateType : special.bindType ) || type; 
  45. //基于新設置的類型更新special 
  46. special = jQuery.event.special[ type ] || {}; 
  47. // handleObj貫穿整個事件處理 
  48. handleObj = jQuery.extend({ 
  49. type: type, 
  50. origType: origType, 
  51. data: data, 
  52. handler: handler, 
  53. guid: handler.guid, 
  54. selector: selector, 
  55. // For use in libraries implementing .is(). We use this for POS matching in `select` 
  56. //"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?://(" + 
  57. //whitespace + "*((?:-//d)?//d*)" + whitespace + "*//)|)(?=[^-]|$)", "i" ) 
  58. //用來判斷親密關系 
  59. needsContext: selector && jQuery.expr.match.needsContext.test( selector ), 
  60. namespace: namespaces.join("."
  61. }, handleObjIn ); 
  62. //初次使用時初始化事件處理器隊列 
  63. if ( !(handlers = events[ type ]) ) { 
  64. handlers = events[ type ] = []; 
  65. handlers.delegateCount = 0; 
  66. //非自定義事件,如果special事件處理器返回false,則只能使用addEventListener/attachEvent 
  67. if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { 
  68. //給元素綁定全局事件 
  69. if ( elem.addEventListener ) { 
  70. elem.addEventListener( type, eventHandle, false ); 
  71. else if ( elem.attachEvent ) { 
  72. elem.attachEvent( "on" + type, eventHandle ); 
  73. //自定義事件綁定 
  74. if ( special.add ) { 
  75. special.add.call( elem, handleObj ); 
  76. if ( !handleObj.handler.guid ) { 
  77. handleObj.handler.guid = handler.guid; 
  78. //將事件對象handleObj添加到元素的處理列表,代理計數遞增 
  79. if ( selector ) { 
  80. handlers.splice( handlers.delegateCount++, 0, handleObj ); 
  81. else { 
  82. handlers.push( handleObj ); 
  83. //跟蹤那個事件曾經被使用過,用于事件優化 
  84. jQuery.event.global[ type ] = true
  85. //防止ie內存泄漏 
  86. elem = null

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

 

 
  1. <div id="#center"></div> 
  2. <script> 
  3. function dohander(){console.log("dohander")}; 
  4. function dot(){console.log("dot");} 
  5. $(document).on("click",'#center',dohander) 
  6. .on("click",'#center',dot) 
  7. .on("click",dot); 
  8. </script> 

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

 

 
  1. elemData = jQuery._data( elem ); 
  2. elemData = { 
  3.   events: { 
  4.     click: {//Array[3] 
  5.       0: { 
  6.         data: undefined/{...}, 
  7.         guid: 2, //處理函數的id 
  8.         handler: function dohander(){…}, 
  9.         namespace: ""
  10.         needsContext: false
  11.         origType: "click"
  12.         selector: "#center",//選擇器,用來區分不同事件源 
  13.         type: "click" 
  14.       } 
  15.       1: { 
  16.         data: undefined/{...}, 
  17.         guid: 3, 
  18.         handler: function dot(){…}, 
  19.         namespace: ""
  20.         needsContext: false
  21.         origType: "click"
  22.         selector: "#center"
  23.         type: "click" 
  24.       } 
  25.       2: { 
  26.         data: undefined, 
  27.         guid: 3, 
  28.         handler: function dot(){…}, 
  29.         namespace: ""
  30.         needsContext: false
  31.         origType: "click"
  32.         selector: undefined, 
  33.         type: "click" 
  34.       } 
  35.       delegateCount: 2,//委托事件數量,有selector的才是委托事件 
  36.       length: 3 
  37.     } 
  38.   } 
  39.   handle: function ( e ) {…}/*事件處理主入口*/
  40.     elem: document//屬于handle對象的特征 
  41.   } 

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的事件結構就清楚了。后面再分析事件的綁定和觸發以及委托原理。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩成人激情在线| 亚洲精品99久久久久| 日韩天堂在线视频| 欧美综合激情网| 国产欧美日韩精品在线观看| 日本一区二区在线播放| 午夜精品一区二区三区av| 国产精品久久久久久亚洲影视| 亚洲精品成人av| 日韩大片在线观看视频| 日韩精品中文字幕在线观看| 国色天香2019中文字幕在线观看| 97国产精品免费视频| 国产第一区电影| 久久人人看视频| 亚洲老头老太hd| 成人xvideos免费视频| 成人激情视频小说免费下载| 日韩欧美高清在线视频| 国产精品电影网站| 国产成人精品一区二区| 91色p视频在线| 久热精品视频在线观看一区| 国产福利精品在线| 中文字幕欧美日韩| 日韩av影片在线观看| 欧亚精品在线观看| 亚洲色图综合网| 欧美亚洲另类视频| 伦伦影院午夜日韩欧美限制| 久久久国产精品一区| 欧美麻豆久久久久久中文| www.亚洲成人| 亚洲精品福利视频| 欧美极品少妇xxxxx| 国产精品久久久久久久久久| 欧美另类xxx| 中文字幕在线成人| 成人久久久久久| 伊人久久大香线蕉av一区二区| 亚洲sss综合天堂久久| 在线精品国产成人综合| 精品日韩中文字幕| 久久激情五月丁香伊人| 韩国v欧美v日本v亚洲| 欧美国产日本在线| 欧美激情va永久在线播放| 欧美午夜www高清视频| 日韩电影中文字幕一区| 日韩av免费在线观看| 日韩电影中文字幕一区| 中文字幕精品一区久久久久| 色偷偷av一区二区三区乱| 国产激情999| 大胆人体色综合| 午夜剧场成人观在线视频免费观看| 亚洲成人999| 日韩一区二区精品视频| 精品国产一区二区三区久久狼黑人| 欧美中文字幕在线| 精品呦交小u女在线| 日产精品99久久久久久| 超碰91人人草人人干| 久久久最新网址| 97婷婷涩涩精品一区| 亚洲国产精品成人av| 国产精品高潮呻吟视频| 国产精品免费福利| 国内精品久久久久久中文字幕| 欧美电影免费播放| 亚洲精选一区二区| 国产成人精品久久二区二区91| 中文字幕亚洲一区在线观看| 92版电视剧仙鹤神针在线观看| 青青草成人在线| 国内精品久久久久伊人av| 91精品美女在线| 91在线视频精品| 久久久国产精彩视频美女艺术照福利| 久久久av一区| 亚洲第一中文字幕在线观看| 日韩精品极品在线观看| 久久久久久久久久久人体| 97色在线播放视频| 久久久久久久久久久av| 欧美日韩中文字幕| 久久精品2019中文字幕| 国产精品久久久久久av| 欧美日本亚洲视频| 一区二区欧美日韩视频| 欧美人与性动交a欧美精品| 国产亚洲成av人片在线观看桃| 国产成人高清激情视频在线观看| 日韩av在线影视| 成人激情视频小说免费下载| 亚洲va国产va天堂va久久| 国产一区二区三区在线播放免费观看| 国外成人性视频| 国产精品中文字幕在线| 日韩美女在线观看| 日韩激情av在线播放| 中文字幕亚洲激情| 亚洲精品美女视频| 久久视频在线直播| 亚洲小视频在线观看| 久久99久国产精品黄毛片入口| 欧美一级高清免费播放| 日韩欧美国产高清91| 亚洲精品理论电影| 成人免费网站在线观看| 97国产精品人人爽人人做| 欧美综合激情网| 国产精品一久久香蕉国产线看观看| 亚洲欧美另类国产| 成人网页在线免费观看| 日韩一二三在线视频播| 日韩**中文字幕毛片| 日韩欧美国产成人| 欧美另类极品videosbest最新版本| 日本一区二区在线免费播放| 中文字幕亚洲无线码在线一区| 亚洲综合小说区| 国产亚洲激情视频在线| 精品国产一区二区在线| 日本一区二区在线播放| 欧美成人国产va精品日本一级| 中文字幕av一区二区| 自拍视频国产精品| 日韩中文在线不卡| 欧美黑人xxx| 亚洲第一级黄色片| 日韩av电影在线免费播放| 2019国产精品自在线拍国产不卡| 日韩视频免费观看| 国产精品草莓在线免费观看| 欧美性视频在线| 性色av一区二区咪爱| 欧美老女人性视频| 日韩电影大全免费观看2023年上| 欧美怡红院视频一区二区三区| 色综合色综合久久综合频道88| 国产欧美日韩免费看aⅴ视频| 91在线视频导航| 日韩成人激情影院| 日本久久久久亚洲中字幕| 久久久久久综合网天天| 欧美黑人狂野猛交老妇| 久久久久亚洲精品| 日韩电影免费观看在线观看| 亚洲最大的成人网| 成人黄色大片在线免费观看| 97超碰色婷婷| 久久视频在线视频| 国产日产久久高清欧美一区| 欧美一级大胆视频| 国产精品一区二区久久久久| 国产精品第一视频| 姬川优奈aav一区二区| 国产精品第一区| 欧美日韩久久久久| 亚洲欧美日本另类| 久久久久久久国产精品| 久久久久久亚洲精品| 久久精品视频在线播放|