欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery-1.9.1源碼分析系列(十)事件系統(tǒng)之事件體系結(jié)構(gòu)

 更新時(shí)間:2015年11月19日 09:34:40   投稿:mrr  
這篇文章主要介紹了jQuery-1.9.1源碼分析系列(十)事件系統(tǒng)之事件體系結(jié)構(gòu)的相關(guān)資料,需要的朋友可以參考下

又是一個(gè)重磅功能點(diǎn)。

  在分析源碼之前分析一下體系結(jié)構(gòu),有助于源碼理解。實(shí)際上在jQuery出現(xiàn)之前,Dean Edwards的跨瀏覽器AddEvent()設(shè)計(jì)做的已經(jīng)比較優(yōu)秀了;而且jQuery事件系統(tǒng)的設(shè)計(jì)思想也是基于該思想的,所以我們先分析一下Dean Edwards前輩的事件綁定。

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

  源碼解讀

//事件添加方法
function addEvent(element, type, handler) {
  //保證每個(gè)不同的事件響應(yīng)函數(shù)只有唯一一個(gè)id
 if (!handler.$$guid) handler.$$guid = addEvent.guid++;
  // 給element維護(hù)一個(gè)events屬性,初始化為一個(gè)空對(duì)象。 
 // element.events的結(jié)構(gòu)類似于 { "click": {...}, "dbclick": {...}, "change": {...} } 
 if (!element.events) element.events = {};
  // 試圖取出element.events中當(dāng)前事件類型type對(duì)應(yīng)的對(duì)象(這個(gè)對(duì)象更像數(shù)組),賦值給handlers
  //如果element.events中沒(méi)有當(dāng)前事件類型type對(duì)應(yīng)的對(duì)象則初始化
 var handlers = element.events[type];
  if (!handlers) {
     handlers = element.events[type] = {};
     // 如果這個(gè)element已經(jīng)有了一個(gè)對(duì)應(yīng)的事件的響應(yīng)方法,例如已經(jīng)有了onclick方法
  // 就把element的onclick方法賦值給handlers的0元素,此時(shí)handlers的結(jié)構(gòu)就是:
  // { 0: function(e){...} },這也是為什么addEvent.guid初始化為1的原因,預(yù)留看為0的空間;
  // 此時(shí)element.events的結(jié)構(gòu)就是: { "click": { 0: function(e){...} }, /*省略其他事件類型*/ } 
  if (element["on" + type]) {
   handlers[0] = element["on" + type];
  }
 }
  // 把當(dāng)前的事件handler存放到handlers中,handler.$$guid = addEvent.guid++; addEvent.guid = 1; 肯定是從1開(kāi)始累加的 
  //因此,這是handlers的結(jié)構(gòu)可能就是 { 0: function(e){...}, 1: function(){}, 2: function(){} 等等... }
 handlers[handler.$$guid] = handler;
  //下文定義了一個(gè)handleEvent(event)函數(shù),將這個(gè)函數(shù),綁定到element的type事件上作為事件入口。
  //說(shuō)明:在element進(jìn)行click時(shí),將會(huì)觸發(fā)handleEvent函數(shù),handleEvent函數(shù)將會(huì)查找element.events,并調(diào)用相應(yīng)的函數(shù)??梢园裩andleEvent稱為“主監(jiān)聽(tīng)函數(shù)”
 element["on" + type] = handleEvent;
};
//計(jì)數(shù)器
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是響應(yīng)事件的節(jié)點(diǎn),這個(gè)接點(diǎn)上有events屬性(在addEvent中添加的)
  //獲取節(jié)點(diǎn)對(duì)應(yīng)事件響應(yīng)函數(shù)列表
 var handlers = this.events[event.type];
 // 循環(huán)響應(yīng)函數(shù)列表執(zhí)行
  for (var i in handlers) {
   //保持正確的作用域,即this關(guān)鍵字
     this.$$handleEvent = handlers[i];
  this.$$handleEvent(event);
 }
};

 重新梳理一下數(shù)據(jù)結(jié)構(gòu),使用一個(gè)例子

<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);

  經(jīng)過(guò)addEvent()函數(shù)之后,當(dāng)前的數(shù)據(jù)結(jié)構(gòu)為:

element: {
  onclick: handleEvent(event), //click事件的主監(jiān)聽(tīng)函數(shù)
  onchage: handleEvent(event),  //change事件的主監(jiān)聽(tīng)函數(shù)
  events: {
    click:{//這是一個(gè)類數(shù)組
      0: f0, //element已有的事件
      1: f1,  //下標(biāo)1實(shí)際上就是f1.$$guid
      3: f3 //下標(biāo)3實(shí)際上就是f3.$$guid,需要注意的是每一個(gè)響應(yīng)事件都有一個(gè)唯一的$$guid作為下標(biāo) 
      ...
    },
    change:{//這是一個(gè)類數(shù)組
      1: f1,
      2: f2,
      3: f3
    }
 }
}

  事件系統(tǒng)會(huì)根據(jù)調(diào)用addEvent的順序給每個(gè)響應(yīng)函數(shù)(也就是addEvent(element, type, handler)中的第三個(gè)參數(shù)handler)打上標(biāo)記$$guid。源碼

  //保證每個(gè)不同的事件響應(yīng)函數(shù)只有唯一一個(gè)id
 if (!handler.$$guid) handler.$$guid = addEvent.guid++;

  最終三個(gè)響應(yīng)函數(shù)的$$guid標(biāo)記分別是

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

  而根據(jù)源碼中

handlers[handler.$$guid] = handler;

  那么某一個(gè)函數(shù)在任何事件響應(yīng)函數(shù)集合中的下標(biāo)位置是固定的。比如click和change事件都調(diào)用f3作為響應(yīng)事件,那么f3在element.events.click以及element.events.change中的下標(biāo)位置都是f3.$$guid = 3;即element.events.click[3] = element.events.change[3] = f3。

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

  這樣的設(shè)計(jì),其實(shí)已經(jīng)具備了jquery事件系統(tǒng)的雛形,包含了幾個(gè)最主要的特點(diǎn):

  1)element上的所有事件,將保存到element.events屬性中,不是直接綁定到element上;這樣一個(gè)事件可以有無(wú)數(shù)個(gè)響應(yīng)函數(shù)。

  2)handleEvent作為element所有事件的“主監(jiān)聽(tīng)函數(shù)”,有它統(tǒng)一管理element上的所有函數(shù)。

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

  好啦,明白了addEvent的事件結(jié)構(gòu),這個(gè)想法確實(shí)讓人覺(jué)得眼前一亮。下面分析jQuery的事件結(jié)構(gòu)

b. jQuery的事件結(jié)構(gòu)

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

  源碼分析

add: function( elem, types, handler, data, selector ) {
 var tmp, events, t, handleObjIn,
  special, eventHandle, handleObj,
  handlers, type, namespaces, origType,
  //獲取elem節(jié)點(diǎn)對(duì)應(yīng)的緩存數(shù)據(jù)
  elemData = jQuery._data( elem );
 //沒(méi)有數(shù)據(jù)或文本/注釋節(jié)點(diǎn)不能附加事件(但是允許附加普通對(duì)象)
 if ( !elemData ) {
  return;
 }
 //調(diào)用者能通過(guò)自定義數(shù)據(jù)替換handler
 if ( handler.handler ) {
  handleObjIn = handler;
  handler = handleObjIn.handler;
  selector = handleObjIn.selector;
 }
 //確保handler函數(shù)有唯一的ID,后續(xù)會(huì)用來(lái)查找/刪除這個(gè)handler函數(shù)
 if ( !handler.guid ) {
  handler.guid = jQuery.guid++;
 }
 //如果是初次進(jìn)入,初始化元素的事件結(jié)構(gòu)和主事件響應(yīng)入口
 if ( !(events = elemData.events) ) {
  events = elemData.events = {};
 }
 if ( !(eventHandle = elemData.handle) ) {
  eventHandle = elemData.handle = function( e ) {
   //當(dāng)一個(gè)事件被調(diào)用后頁(yè)面已經(jīng)卸載,則放棄jQuery.event.trigger()的第二個(gè)事件,
   return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
   jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
   undefined;
  };
  //將elem作為handle函數(shù)的一個(gè)特征防止ie非本地事件引起的內(nèi)存泄露
  eventHandle.elem = elem;
 }
 //多個(gè)事件使用空格隔開(kāi)的處理
  //如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事件處理器來(lái)處理更改后的事件類型
  special = jQuery.event.special[ type ] || {};
  //如果選擇器已定義,確定special事件API類型,否則給他一個(gè)類型
  type = ( selector ? special.delegateType : special.bindType ) || type;
  //基于新設(shè)置的類型更新special
  special = jQuery.event.special[ type ] || {};
  // handleObj貫穿整個(gè)事件處理
  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" )
   //用來(lái)判斷親密關(guān)系
   needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
   namespace: namespaces.join(".")
  }, handleObjIn );
  //初次使用時(shí)初始化事件處理器隊(duì)列
  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;
   }
  }
  //將事件對(duì)象handleObj添加到元素的處理列表,代理計(jì)數(shù)遞增
  if ( selector ) {
   handlers.splice( handlers.delegateCount++, 0, handleObj );
  } else {
   handlers.push( handleObj );
  }
  //跟蹤那個(gè)事件曾經(jīng)被使用過(guò),用于事件優(yōu)化
  jQuery.event.global[ type ] = true;
 }
 //防止ie內(nèi)存泄漏
 elem = null;
}

  依然用實(shí)例來(lái)說(shuō)明jQuery的事件結(jié)構(gòu)

<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>

  經(jīng)過(guò)添加處理環(huán)節(jié),事件添加到了元素上,而且節(jié)點(diǎn)對(duì)應(yīng)的緩存數(shù)據(jù)也添加了相應(yīng)的數(shù)據(jù)。結(jié)構(gòu)如下

elemData = jQuery._data( elem );
elemData = {
  events: {
    click: {//Array[3]
      0: {
        data: undefined/{...},
        guid: 2, //處理函數(shù)的id
        handler: function dohander(){…},
        namespace: "",
        needsContext: false,
        origType: "click",
        selector: "#center",//選擇器,用來(lái)區(qū)分不同事件源
        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,//委托事件數(shù)量,有selector的才是委托事件
      length: 3
    }
  }
  handle: function ( e ) {…}/*事件處理主入口*/{
    elem: document//屬于handle對(duì)象的特征
  }
}

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

  jQuery做了哪些改進(jìn)?

  1)事件數(shù)據(jù)不再直接保存在節(jié)點(diǎn)上,而是使用jQuery緩存系統(tǒng)內(nèi)(內(nèi)部使用的緩存jQuery._data方式存?。?/p>

  2)事件委托:綁定到當(dāng)前節(jié)點(diǎn)(例子中當(dāng)前節(jié)點(diǎn)是document根節(jié)點(diǎn))的處理函數(shù)不僅僅包含當(dāng)前節(jié)點(diǎn)觸發(fā)事件(click)響應(yīng)時(shí)處理的事件(例子中selector為undefined時(shí)對(duì)應(yīng)的處理函數(shù)dot);還代理了其他節(jié)點(diǎn)(例子中的#center節(jié)點(diǎn))觸發(fā)事件(click)響應(yīng)時(shí)處理的事件(例子中selector為"#center"對(duì)應(yīng)的處理事件doHandler和dot);委托機(jī)制在后續(xù)分析。

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

  到此jQuery的事件結(jié)構(gòu)就清楚了。后面再分析事件的綁定和觸發(fā)以及委托原理。

相關(guān)文章

  • jQuery EasyUI 開(kāi)源插件套裝 完全替代ExtJS

    jQuery EasyUI 開(kāi)源插件套裝 完全替代ExtJS

    JQuery愛(ài)好者們的福音 jQuery EasyUI 開(kāi)源插件套裝 完全替代ExtJS(引用)
    2010-03-03
  • 基于JQuery的日期聯(lián)動(dòng)實(shí)現(xiàn)代碼

    基于JQuery的日期聯(lián)動(dòng)實(shí)現(xiàn)代碼

    基于JQuery的日期聯(lián)動(dòng)實(shí)現(xiàn)代碼,需要的朋友可以參考下。
    2011-02-02
  • jquery hover 不停閃動(dòng)問(wèn)題的解決方法(亦為stop()的使用)

    jquery hover 不停閃動(dòng)問(wèn)題的解決方法(亦為stop()的使用)

    下面小編就為大家?guī)?lái)一篇jquery hover 不停閃動(dòng)問(wèn)題的解決方法(亦為stop()的使用)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02
  • jQuery扇形定時(shí)器插件pietimer使用方法詳解

    jQuery扇形定時(shí)器插件pietimer使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了jQuery扇形定時(shí)器插件pietimer的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • jQuery ready()和onload的加載耗時(shí)分析

    jQuery ready()和onload的加載耗時(shí)分析

    這篇文章主要介紹了jQuery ready()和onload的加載耗時(shí),結(jié)合實(shí)例形式對(duì)比分析了jQuery中的ready()和JavaScript的window.onload進(jìn)行頁(yè)面加載的時(shí)間,需要的朋友可以參考下
    2016-09-09
  • JQuery中的ready函數(shù)沖突的解決方法

    JQuery中的ready函數(shù)沖突的解決方法

    jQuery確實(shí)是一個(gè)提高前端開(kāi)發(fā)效率的好框架(雖然很多大牛們都說(shuō)它效率不咋地),但是用好它有時(shí)候并不容易,也許你也遇到過(guò)以下情況
    2010-05-05
  • 使用Browserify配合jQuery進(jìn)行編程的超級(jí)指南

    使用Browserify配合jQuery進(jìn)行編程的超級(jí)指南

    這篇文章主要介紹了使用Browserify配合jQuery進(jìn)行編程的超級(jí)指南,Browserify 可以讓你使用類似于node的require()的方式來(lái)組織瀏覽器端的JavaScript代碼,需要的朋友可以參考下
    2015-07-07
  • jQuery實(shí)現(xiàn)簡(jiǎn)單輪播圖效果

    jQuery實(shí)現(xiàn)簡(jiǎn)單輪播圖效果

    這篇文章主要為大家詳細(xì)介紹了jQuery實(shí)現(xiàn)簡(jiǎn)單輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • 最新評(píng)論