麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 語言 > 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三级 | 国产一级在线观看视频 | 久久久综合 | 男女羞羞视频在线观看免费 | 中文字幕在线一 | 黄视频在线网站 | 成人毛片视频免费看 | 最新影院 | 久久久久女人精品毛片九一 | 亚洲性生活免费视频 | 性插视频| 成人性视频欧美一区二区三区 | 亚洲自拍第一 | 国产一国产一级毛片视频在线 | 国产成人在线观看免费 | 黄色特级毛片 | 日韩精品中文字幕在线播放 | 日本网站在线播放 | av噜噜噜噜 | 成人免费自拍视频 | 欧美成人一级片 | 在线播放视频一区二区 | 国产精品久久久久久久久久妇女 | 免费观看黄色一级视频 | 久章草影院 | 亚洲成人免费影视 | 色婷婷a v | 天堂在线中文资源 | 亚洲乱搞| 福利在线国产 | 国产成人精品一区在线播放 | 欧美精品v国产精品v日韩精品 | 91一区二区三区久久久久国产乱 | 国产精品久久久久久婷婷天堂 | 狠狠久久 | 精品国产乱码久久久久久久久 | 久久免费观看一级毛片 | 毛片大全|