jQuery 類庫的整體設計

說明:14年研究jquery源碼的總結node

1.整體設計

本文對jquery1.7版本進行了閱讀學習,將整個jquery源碼拆分爲11個模塊,這些模塊相互依賴,構成了一個簡單、強大的js類庫。jquery是一個基於DOM操做的類庫,所以Sizzle選擇器引擎的實現就顯得尤其重要。針對Sizzle選擇器引擎的實現,以前已經作過先關的分析,參見:sizzle選擇器引擎介紹 。下面對其中的數據存儲、事件處理、異常請求ajax、動畫等進行簡單的介紹。jquery

圖片描述

(function(window, undefined) {
    var jQuery = function(selector, context) {
        return new jQuery.fn.init(selector, context, rootJquery);
    }
    jQuery.fn = function() {
        // 原型對象
        ...
    }
    // 工具方法
    // 異步隊列
    // 隊列queue
    // 瀏覽器測試support
    // 屬性操做
    // 事件系統
    // DOM遍歷與操做、樣式操做
    // ajax請求
    // 動畫
})(window)

2. 數據存儲

圖片描述

在實際的項目開發中,常常須要把某些信息附加到一個DOM節點中,那麼若是管理DOM節點和附加數據的關係,就顯得很是重要。很明顯,目前有兩種思路來解決這個問題:ajax

  • 1)直接附加在DOM節點上編程

  • 2)經過一個id來關聯DOM節點、附加數據。這兩種方法各有利弊:方法1的好處是DOM節點、附加數據在一塊兒,便於維護;方法2的好處是能夠避免相互依賴,從而避免內存泄露問題。json

var arr = [];
function createNode() {
     return document.createElement('div');
}
function saveNodes() {
    for(var i=0; i<100; i++) {
        arr.push(createNode());
    }
}
// 將上述100個nodes節點渲染到頁面,以後在將其從頁面中剔除掉,那麼由於arr引用着100個nodes節點,這些節點就沒法被垃圾回收機制回收,從而引發內存泄露問題。

3. 異步Deferred

jQuery中關於異步的實現,大體上遵循Promise/A規範,爲何說是大體呢?segmentfault

  • then的返回:then方法並無返回新的異步對象,這不符合Promise/A規範中的then要求。這一點在jQuery2.0之後已經被修改跨域

  • 結果處理:在進行結果處理的時候,jQuery並無進行結果的異常捕獲數組

  • 參數的個數:jquery中resolve能夠有多個參數,而Promise/A的resolve僅能有一個瀏覽器

內部實現比較曲折抽象,代碼晦澀難懂,主要是經過"once"、"memory"兩個參數進行控制:"once"決定異步的回調函數只能被執行一次;「memory」決定函數具備記憶動能,也就是異步事件完成之後,再綁定回調函數,回調函數會當即執行。緩存

4. 事件處理

圖片描述

jquery內部事件的功能很強大,除了能夠處理DOM事件外,還能夠自定義事件、觸發事件(DOM事件、自定義事件)、定義事件的命名空間等強大功能。而且jquery內部的另一大亮點就是經過數據存儲模塊(.data),儘可能下降DOM和監聽事件之間的依賴,避免DOM、js對象相互依賴形成的內存泄露。在數據緩存模塊的數據結構以下:

$('.a').on('click', function() {});
// results is as fellow:
$.cache = {
    1: {
        events: {               
            click: [                //click.delegateCount: 記錄代理事件的個數,代理回調函數放在數組的前面
                {
                    data: ...,
                    guid: ...,
                    selector: ...,
                    handler: function() {}, // handler.guid = 1  用於定位和移除監聽函數
                    ....
                }  
            ]
        },
        handle: function() {......}    // 主監聽函數
    }
}

4.1 事件綁定

當綁定事件時,內部方法的調用鏈爲:bind/delegate/live/one()--->.on()—>$.event.add()—>$.data()/addEventListener/attachEvent()。其實在對於一個DOM元素,全部的事件都對應一個主監聽函數($._data(elem).handle),而後經過主監聽函數經過事件分發函數($.event.dispatch)來觸發相應類型的監聽函數。

$('.a').on('click', fn1);
$('.a').on('blur', fn2);
$('.a').on('focus', fn3);
// 其實DOM元素(.a)並無直接與fn一、fn二、fn3關聯起來,而是經過dispatch進行事件分發
// 用僞代碼能夠表示以下:
$('.a').on('click blur focus', dispatch);
function dispatch(type) {
    var fn;
    if(type == 'click') {
        fn = fn1;  
    } else if(type= == 'blur') {
        fn = fn2;
    } else if (type == 'focus') {
        fn = fn3;
    }
    return fn;
}

4.2 事件移除

當移除事件時,內部方法的調用鏈爲:unbind/delagate/die()--->.off()—>$.event.remove()—>$._data()/removeEventListener/detachEvent()。事件的移除,也就是從數據存儲對象$.cache中移除相應的事件對象,當事件對象events爲空時,則移除整個數據緩存對象。

4.3 事件手工觸發

在jquery中,能夠手工觸發DOM事件或自定義事件。內部方法的調用鏈爲:.trigger/triggerHandler() —> $.event.trigger() — > $.event.dispatch(主監聽函數) — >事件的監聽函數。

還有一個須要關注的問題就是,如何事件事件的冒泡呢?方法其實很簡單,就是根據DOM的結構向上查詢出元素的祖先元素,一直到window對象,這樣就構成了元素的冒泡路徑,而後觸發這個路徑上元素的相應事件。這也是jquery能夠模擬focus、blur、change、submit進行事件冒泡的關鍵環節。

// 尋找冒泡路徑
var eventPath = [];
for(;cur; cur = cur.parentNode) {
    eventPath.push([cur, type]);
}
// 執行路徑上的監聽函數
for() {
    cur = eventPath[i][0];
    type =  eventPath[i][4];
    hanle = $._data(cur, "events")[type];
    hanle.apply(cur);
}

5. 異步請求ajax

圖片描述

異步請求是jquery在整體能夠分爲三部分:核心實現、便捷方法、ajax全局事件。其中該模塊依賴於Deferred模塊提供的異步編程模塊,能夠方便進行回調函數的註冊,例如:

$(url, options).then(successFn, failFn);

其中核心方法的實現主要包括如下驟:

  • 參數的設置:在jQuery全部API中,.ajax的參數種類應該是對多的,裏面的參數看得人掩護繚亂。可是其中最終的有url、type、dataType、data,尤爲是dataType的設置對於結果的應該很長大,因此有大量代碼是對這一步的處理

  • 前置過濾函數處理:主要是對json、jsonp、script三種數據類型的處理,在請求發送前對其進行過濾處理

  • 請求發出:這裏請求的發出包括兩種方式,分別爲依賴於XMLHttpRequest和script標籤。若是瀏覽器容許跨域,則僅需使用XMLHttpRequest就足夠了

  • 回調函數的執行:這裏的回調函數包括不少種,請求發出前、開始接受數據、數據接受完成等等

6. 動畫解析

圖片描述

在jQuery中,動畫show、hide、fadeIn、fadeOut等均要調用Animation方法,也就是說Animation是最基本的入口函數。在上圖中能夠看到該入口函數包括了三個過程:參數配置、生成動畫函數、動畫函數執行。下面展開包括如下細節:

  • 參數配置:主要有三個參數,duration表示動畫的執行時間;easing爲動畫每一幀的變化速度,目前jQuery中僅存在兩種幀變化函數:線性(linear)變化、餘弦變化(swing),要用其餘變化函數只能修改$.fx.easing對象;complete爲動畫完成以後須要執行的回調函數

  • 生成動畫函數doAnimation:jQuery會給每個樣式生成一個$.fx對象,該對象用於實現動畫效果。默認狀況下,每隔13ms會執行一幀動畫,而後更新頁面的樣式

  • 動畫執行:若是動畫不須要排隊,在生成完動畫函數以後,就當即執行動畫函數doAnimation;反之,將doAnimation放入隊列queue中進行排隊。當全部動畫均完成以後,就能夠執行回調函數complete了。

固然,jQuery內部實現比較複雜,考慮了函數暫停、樣式臨時修改(修改inline元素的width/height時,會臨時將其display修改成inline-block)、清空動畫隊列等操做。

相關文章
相關標籤/搜索