1. 程式人生 > >jQuery 事件機制原始碼分析1——jQuery事件機制整體架構

jQuery 事件機制原始碼分析1——jQuery事件機制整體架構

之前在網上搜索過一些內容大多都是寫的零散寫出一些方法感覺不是很系統,要不就是文不對題,後來自己debug 一番大概搞清楚了,決定寫個文章記錄下來。

需要說明的有以下幾點:

1 本文不會教你如何使用.on .off .trigger 等方法 

2  基於原始碼分析基於自己見解,有錯請指出

3 因為整個事件機制過於複雜 計劃分幾篇寫完,

4 本文jQuery 庫版本為1.11.2,可以下載 未壓縮版對應本文中提到的行數,

5 因單純的debug 過於枯燥,且易一葉障目,本文計劃從以下幾個方面來闡述,

     1)jQuery事件機制的整體結構

     2)標準事件繫結

     3)事件名稱空間處理

     4)  事件委託處理

     5)自定義事件

     6) 事件物件封裝

     7) 事件分發

6 因工作原因本文會陸續更新並修改調整


【jQuery事件機制整體架構】

在講正文之前,我們可以思考以下幾個問題:

     1)標準事件 jQuery 底層是如何進行繫結的?

     2) 自定義事件 jQuery 底層是如何進行繫結

     3) jQuery 繫結的事件如何觸發的

     4) 回撥函式中Event物件的 data 資料怎麼帶過去的

     5) 事件委託是如何實現的

在沒有分析程式碼前,我一唯一確定的,標準事件一定是addEventListener / attachEvent ,寫這麼多廢話,帶著這些問題可以更有目標性的閱讀本文,


【jQuery 事件架構】

從簡單開如,通常我們會寫下這樣類似的程式碼

$(document).on("click",function(){
    alert(1)
});

     .on 方法是事件繫結的開始,其實它在背後做了很多看不到的事情,它的做的事情有哪些呢?拿本例來講

     1) 它會在jQuery.cache 中建立一個物件,這個物件記錄了與本次繫結事件相關的一切資訊  



問題來了,jQuery.cache  又是什麼?見3882行

jQuery.extend({
   cache: {},
在jQuery.cache 中建立這個物件有什麼用?或者說如何用?後面會講到,現在先說更重要的事情,但我可以很負責的的告訴你,只要你清掉了jQuery.cache我保證你頁面繫結事件絕對會失效(先這樣理解),那麼很明顯,jQuery 不是把回撥函式在底層直接繫結,否則只重寫,jQuery.cache 怎麼可能會讓事件繫結失效呢?jQuery 在底層的事件繫結方法確實是,addEventListener / attachEvent  見4378~4384

// Bind the global event handler to the element
if ( elem.addEventListener ) {
   elem.addEventListener( type, eventHandle, false );

} else if ( elem.attachEvent ) {
   elem.attachEvent( "on" + type, eventHandle );
}
但它繫結的回撥函式是什麼呢?見 4326~4336行

if ( !(eventHandle = elemData.handle) ) {
   eventHandle = elemData.handle = function( e ) {
      // Discard the second event of a jQuery.event.trigger() and
      // when an event is called after a page has unloaded
      return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
         jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
         undefined;
   };
   // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
   eventHandle.elem = elem;
}
其實無論是document 還是div  或是其它別的東西,它都會用原生JS以這個方法為回撥函式,即便清掉jQuery.cache這個回撥方法其實還是執行的,只不過沒有執行你原始的回撥函式而已。

這個方法用網上其它的文章的的說法稱為:事件分發器。事件分發器?你講什麼鬼東西,標準事件是系統監聽並執行相關回調的,要你來分發什麼?答案就是,分發,jQuery.cache !   我們可以認為在此例中,jQuery 為document元素  綁定了一個事件分發器,當相關事件觸發時,事件分發器,會去jQuery.cache 中找到與本事件相關的資料物件,並執行相關回調,當然其中還包含一些更復雜的細節,比如委託,全名空間等後面會講到,這裡我們先只說大的架構流程。 

現在有個問題,那就是分發器是如何工作的?它是如何找到document元素相關的cahce 物件呢?或者說,在jQuery.cache 中建立的物件是如何與document 建立對映的?

簡單的說,繫結事件時,jQuery 在jQuery.cache 中建立物件的時候,會有一個key ,每個元素只有一個key 且是唯一的,就是上jQuery.cache文圖中的 “1”,jQuery 會把這個key  放在document元素自身中,那麼這個key 放在哪個屬性下呢?

答案就是,

internalKey
我們看程式碼 3709行

internalKey = jQuery.expando,
再看 2309 行

expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
在控制檯敲一下,jQuery.expand 你就會現一個字串,在事件繫結完成後,我們再在控制檯敲一下 document[jQuery.expando]  如圖,

這時你似乎明白了些什麼吧,我們可以下這樣一個結論,只要這個元素用.on綁定了事件(至少有一人沒解綁事件),必定它會有這樣一個看似奇怪的屬性"jQuery111207028145161197981" 這個屬性和jQuery.expando相等 是個常量,此元素的這個屬性中存放了與jQuery.cache 對映的key 通過這個key 分發器就可以從jQuery.cache 中找到與本元素相關的cache物件。然後經過過濾找到相關的回撥函式,並執行。

以上就是大致的一個流程,無論是委託還是直接繫結還是繫結自定義事件,都是這個邏輯,

那麼Event 物件的封裝又在哪裡呢?

Event 物件在事件觸發時進行封裝,新的jQuery Event 物件會複製原生Event 物件的屬性,其它就是消除不同瀏覽器的差異,比如event.target  event.wich event.relatedTarget 等,新增jQuery Event 物件的一些屬性。

事件觸發時分發器大概的工作機制上文大概說了一下,具體的細節處理部分後面會分篇進行細寫,