jQuery 學習筆記:jQuery 程式碼結構

agyyl發表於2018-05-07

jQuery 學習筆記:jQuery 程式碼結構

這是我學習 jQuery 過程中整理的筆記,這一部分主要包括 jQuery 的程式碼最外層的結構,寫出來整理自己的學習成果,有錯誤歡迎指出。

jQuery 的最外層 $,jQuery

(function (global, factory) {
    "use strict";
    if (typeof module === "object" && typeof module.exports === "object") { // 判斷是否使用 commonjs 環境
        module.exports = global.document ?
            factory(global, true) :
            function (w) {
                if (!w.document) {
                    throw new Error("jQuery requires a window with a document");
                }
                return factory(w);
            };
    } else {
        factory(global);
    }
})(typeof window !== "undefined" ? window : this,  function (window, noGlobal){
    if (typeof define === "function" && define.amd) {  // 是否使用 AMD 模組化規範
        define("jquery", [], function () {
            return jQuery;
        });
    }
    var
        _jQuery = window.jQuery,
        _$ = window.$;
    jQuery.noConflict = function (deep) {
        if (window.$ === jQuery) {
            window.$ = _$;
        }
        if (deep && window.jQuery === jQuery) {
            window.jQuery = _jQuery;
        }
        return jQuery;
    };
    if (!noGlobal) {
        window.jQuery = window.$ = jQuery;
    }
    return jQuery;
});

上面是 jQuery-3.3.1 的一部分程式碼,是最外層的一部分,包含了大量的資訊。

(function (global, factory) {
})(typeof window !== "undefined" ? window : this, function (window, noGlobal){
});

jQuery 程式碼包含在一個自執行的函式內,然後返回出一個指向 jQuery 的索引,就形成了一個閉包,利用 jQuery 物件,可以訪問定義在自執行函式內的方法,在外面則訪問不到,防止變數命名同外界產生衝突,同時,window 物件作為引數傳入函式,在函式內,將 jQuery 和 $ 掛載到 window 物件物件上,這樣,在外界也可以直接通過 window.$ 和 window.jQuery 訪問到 jQuery 物件。通常使用較多的 $,實際指的就是 window.$,但通常會省略掉 window 物件。

    if (!noGlobal) {
        window.jQuery = window.$ = jQuery; // 將 jQuery 和 $ 掛載到 window 物件物件上
    }
    return jQuery; // 然後返回出一個指向 jQuery 的索引

同時,為了防止不同庫對 $ 的指向產生衝突,jQuery 還可以將 $ 和 jQuery 還原成原來的指向,只要使用 jQuery.noConflict 方法。

    var
        _jQuery = window.jQuery, // 儲存 window.jQuery 的值
        _$ = window.$; // 儲存 window.$ 的值
    jQuery.noConflict = function (deep) {
        if (window.$ === jQuery) {
            window.$ = _$;
        }
        if (deep && window.jQuery === jQuery) { // 根據引數判斷是否還原 window.jQuery 的值
            window.jQuery = _jQuery;
        }
        return jQuery;
    };

先使用 jQuery 和 $ 儲存 window.jQuery 和 window.$ 的原始值,如果呼叫 jQuery.noConflict 方法,判斷傳入的引數,如果引數值為 false,就僅釋放 $,還原為原來的值,如果引數值為 true,就將 window.jQuery 也還原為原來的值。

jQuery 函式的過載

jQuery 在使用時,既可以作為一個函式,直接呼叫 $,傳入引數,也可以將其作為物件,呼叫物件的方法,作為物件的情況之後再討論,先說將 $ 作為函式使用。

由於 jQuery 中同名函式會覆蓋,傳入引數不同時,不能從多個同名函式中選擇某個函式,而且根據函式名選擇唯一的函式,所以當傳入引數種類不同時,就需要根據引數的個數,型別等資訊判斷具體的處理方式了。當呼叫 jQuery 時,指向的是一個函式。

jQuery = function (selector, context) {
    return new jQuery.fn.init(selector, context);
},

會直接返回一個以 jQuery.fn.init 為建構函式,new 出的物件。而在 jQuery.fn.init 函式中,則會根據傳入的引數的不同,進行不同的處理。

init = jQuery.fn.init = function (selector, context, root) {
        var match, elem;
        if (!selector) { // 如果第一個引數為空,直接 return 呼叫函式的物件
          return this;
        }
        root = root || rootjQuery;
        if (typeof selector === "string") {
            // 引數為字串,進行處理
        } else if (selector.nodeType) {
            // 引數為元素節點,進行處理
        } else if (isFunction(selector)) {
            //引數為函式,進行處理
        }
        return jQuery.makeArray(selector, this);
      };
    init.prototype = jQuery.fn; // 將 init 函式的原型指定為 jQuery.fn,則 init 建構函式 new 出的物件就都可以直接使用 init 函式的原型也就是 jQuery.fn 包含的方法。

當引數為空時,返回 init 函式的呼叫者,最終,會返回 jQuery,即 $ 物件,可以使用物件上的方法。當第一個引數為字串時,可以是選擇器,html程式碼(會建立DOM元素幷包裝成 jQuery 物件),當為 DOM 物件時,包裝成 jQuery 物件,當為函式時,$(fn) 相當於 $(document).ready(fn),會在頁面頁面 DOM 載入完成後呼叫 fn,效能上優於 window.onload。

jQuery 外掛

上面說到,jQuery 可以作為一個物件,使用其繫結的方法,例如 $.trim 方法,可以去除字串兩邊的空格。此外,jQuery 物件的建構函式 init 的原型指向 jQuery.fn,則 DOM 物件被包裝成 jQuery 物件後,就可以呼叫 jQuery.fn 的方法了,例如 $(`p`).css 方法等。這兩種方法都可以新增新的方法,對應了 jQuery 的兩種外掛。

新增外掛使用的是 jQuery.extend 函式,以及 jQuery.fn.extend,其實指向的是同一個函式,只是函式的呼叫者不同,this 指向就不同了。

$.extend 函式本身可以作為一個工具函式,處理物件的合併,語法 jQuery.extend([deep], target, object1, [objectN]),這個函式會判斷第一個引數是否是一個 Boolean 值(可選),如果是,則進行深拷貝,如果不是,則進行淺拷貝。然後根據除了 Boolean 值之外的其他引數的個數,判斷是否是新增外掛,如果剩餘引數不止一個,則第一個作為 target,其他物件的屬性將會複製到 target 上,而如果只有一個引數,則會將這個引數物件的方法新增到 jQuery 物件上,或者 jQuery.fn 上,根據呼叫函式的物件不同決定。例如:

:(function($) {
    $.fn.extend({ // 引數物件
        `color`: function(){ // 物件的屬性將會複製到 $.fn 上
            // 外掛程式碼
        },
        `border`: function () {
            // 外掛程式碼
        }
    });
})(jQuery);

jQuery 本身的程式碼中,也大量使用了 $.extend 方法。這個方法可以和 Object.assign() 對照學習。

資料:

相關文章