2、構造jQuery對象5——靜態屬性和方法

在構造jQuery對象模塊中還定義了一些重要的靜態屬性和方法,它們時其餘模塊實現的基礎。html

// 涉及外部代碼行

// 代碼行:48——124
var arr = [];

var document = window.document;

// 該方法與 setPrototypeOf 方法配套,用於讀取一個對象的 prototype 對象。
var getProto = Object.getPrototypeOf;

var slice = arr.slice;

var concat = arr.concat;

var push = arr.push;

var indexOf = arr.indexOf;

var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var fnToString = hasOwn.toString;

var ObjectFunctionString = fnToString.call( Object );

var support = {};

// isFunction(obj)用於判斷傳入的參數是不是函數
var isFunction = function isFunction(obj) {

    // Support: Chrome <=57, Firefox <=52
    // In some browsers, typeof returns "function" for HTML <object> elements
    // (i.e., `typeof document.createElement( "object" ) === "function"`).
    // We don't want to classify *any* DOM node as a function.
    // nodeType 屬性可返回節點的類型。 
    /* 
    nodeType值-元素類型 
1-ELEMENT 
2-ATTRIBUTE 
3-TEXT 
4-CDATA 
5-ENTITY REFERENCE 
6-ENTITY 
7-PI (processing instruction) 
8-COMMENT 
9-DOCUMENT 
10-DOCUMENT TYPE 
11-DOCUMENT FRAGMENT 
12-NOTATION 
 */
    return typeof obj === "function" && typeof obj.nodeType !== "number";
};

// isWindow(obj)用於判斷傳入的參數是不是window對象,經過檢測是否存在特徵屬性window來實現。
var isWindow = function isWindow(obj) {
    return obj != null && obj === obj.window;
};



// 變量用於保存腳本屬性
var preservedScriptAttributes = {
    type: true,
    src: true,
    noModule: true
};

// 使用 jQuery html() 方法時插入的腳本老是執行的,jQuery 會檢查傳入的內容,並執行其中的每個腳本。
function DOMEval(code, doc, node) {
    doc = doc || document;
    // 設置當前文檔,默認爲document

    var i,
        script = doc.createElement("script");
    // 在文檔中添加script節點

    script.text = code;
    // 給script添加內容

    // 若是存在節點
    if (node) {
        for (i in preservedScriptAttributes) {
            if (node[i]) {
                script[i] = node[i];
            }
        }
    }
    doc.head.appendChild(script).parentNode.removeChild(script);
    // 將代碼放入文檔中,當即執行,而後當即刪除;
}


// 方法toType(obj)用於判斷參數的內建JavaScript類型。
function toType(obj) {
    // 若是參數是undefined或null,返回"undefined"或"null ";
    if (obj == null) {
        return obj + "";
    }

    // Support: Android <=2.3 only (functionish RegExp)
    // 若是參數是JavaScript內部對象,則返回對應的字符串名稱;其餘狀況一概返回"object"或"function"或class2type的類型。
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[toString.call(obj)] || "object" :
        typeof obj;
}
// 代碼行:144
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

// 主代碼行:296——498
jQuery.extend({

    // Unique for each copy of jQuery on the page
    // 經過隨機數(Math.random())給每一個副本都設定一個惟一值,而且經過正則replace( /\D/g, "" )把全部非數字替換成空。\D相似於[^0-9] 非數字。
    expando: "jQuery" + (version + Math.random()).replace(/\D/g, ""),

    // Assume jQuery is ready without the ready module
    // 設定jQuery在模塊未加載的狀況依然加載完畢。
    isReady: true,

    // error(msg),接受一個字符串,拋出一個包含了該字符串的異常。開發插件時能夠覆蓋這個方法,用來顯示更有用或更多的錯誤提示消息。
    error: function (msg) {
        throw new Error(msg);
    },

    // noop()表示一個空函數。當但願傳遞一個什麼也不作的函數時,能夠使用空函數。開發插件時,這個方法能夠作爲可選回調函數的默認值,若是沒有提供回調函數,則執行jQuery.noop()。
    noop: function () {},

    // isPlainObject(object)用於判斷傳入的參數是不是「純粹的對象」,便是否是用對象直接量{}或new Object()建立的對象。
    isPlainObject: function (obj) {
        var proto, Ctor;

        // Detect obvious negatives
        // Use toString instead of jQuery.type to catch host objects
        // 若是參數obj知足如下條件之一,則返回false:
        // 參數obj能夠轉換爲false
        // Object.prototype.toString.call(obj)返回的不是[object Object]
        // 若是不知足以上全部條件,則至少能夠肯定參數obj是對象。
        if (!obj || toString.call(obj) !== "[object Object]") {
            return false;
        }

        // 讀取obj對象的 prototype 對象並賦值給proto
        proto = getProto(obj);

        // Objects with no prototype (e.g., `Object.create( null )`) are plain
        // 若是對象是個普通的對象沒有原型(例如,Object.create(null))則返回true。
        if (!proto) {
            return true;
        }

        // Objects with prototype are plain iff they were constructed by a global Object function
        // 函數hasOwn.call()指向Object.prototype.hasOwnProperty(property),用於檢測對象是否含有執行名稱的非繼承性。
        // fnToString:將函數轉換成字符串 ObjectFunctionString:function Object() { [native code] }
        Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
        // 返回判斷對象是否是經過newObject()方式建立的結果
        return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
    },

    // isEmptyObject(object)用於檢測對象是否爲空(即不包含屬性)
    isEmptyObject: function (obj) {

        /* eslint-disable no-unused-vars */
        // See https://github.com/eslint/eslint/issues/6125
        var name;
        // for-in循環會同時枚舉非繼承屬性和從原型對象繼承的屬性,若是有,則當即返回false,不然默認返回true。
        for (name in obj) {
            return false;
        }
        return true;
    },

    // Evaluates a script in a global context
    // jQuery.globalEval(code):用於在全局做用域中執行JavaScript代碼。
    globalEval: function (code) {
        // code:待執行的JavaScript語句或表達式
        DOMEval(code);
    },

    // 靜態方法jQuery.each()是一個通用的遍歷迭代方法,用於無縫地遍歷對象和數組。對於數組和含有length屬性的類數組對象。改方法經過下標遍歷,從0到length-1;對於其餘對象則經過屬性名遍歷(for-in),在遍歷過程當中,若是回調函數返回false,則結束遍歷。
    each: function (obj, callback) {
        // obj:待遍歷的數組或對象。
      // callback:回調函數,會在數組的每一個元素或對象的每一個屬性上執行。
        var length, i = 0;

        // 對於數組或類數組對象,經過for循環遍歷下標。
        if (isArrayLike(obj)) {
            length = obj.length;
            for (; i < length; i++) {
                // 執行回調函數時傳入3個參數:元素、下標、元素
                if (callback.call(obj[i], i, obj[i]) === false) {
                    break;
                }
            }
            // 對於對象用for-in循環遍歷屬性名。
        } else {
            for (i in obj) {
                // 執行回調函數時傳入連個參數:對應的屬性值、下標或屬性名、對應的屬性值。
                if (callback.call(obj[i], i, obj[i]) === false) {
                    break;
                }
            }
        }
        // 返回obj
        return obj;
    },

    // Support: Android <=4.0 only
    // jQuery.trim(text):用於移除字符串開頭和結尾的空白符。若是是null返回空字符串,不然將text轉成字符串,經過replace()中正則表達式rtrim匹配的子串並返回。
    trim: function (text) {
        return text == null ?
            "" :
            (text + "").replace(rtrim, "");
    },

    // results is for internal usage only
    // makeArray能夠將一個類數組轉換成真正的數組。若是傳入第二個參數resullts(僅在jQuery內部使用),第一個參數arr中的元素將被合併入第二個參數,最後返回第二個參數,此時返回的不必定是真正的數組。
    makeArray: function (arr, results) {
        // arr:待轉換對象,能夠是任何類型
        // results:僅在jQuery內部使用。若是傳入參數resultes,則在該參數上添加元素。

        // 定義返回值ret。若是傳入了參數result則把該參數做爲返回值,不然新建一個空數組做爲返回值。
        var ret = results || [];

        // 若是傳入的參數arr不是null、undefined的狀況
        if (arr != null) {
            // 使用isArrayLike判斷arr是不是數組或類數組對象。若是是數組或類數組對象,調用jQuery.merge()將arr合併到返回值ret中
            if (isArrayLike(Object(arr))) {
                jQuery.merge(ret,
                    typeof arr === "string" ? [arr] : arr
                );
                // 不然,由於不肯定ret的格式。因此選擇push.call()的方式合併而不是ret.push()。若是隻傳入參數array,則返回值ret是真正的數組;若是還傳入了第二個參數result,則返回值ret的類型取決於該參數的類型。
            } else {
                push.call(ret, arr);
            }
        }

        // 返回ret
        return ret;
    },

    // 在數組中查找指定的下標並返回其下標。elem要查找的值,arr將遍歷的數組,i指定查找的位置。
    inArray: function (elem, arr, i) {
        // elem:要查找的值
        // arr:數組,將遍歷這個數組來查找參數value在其中的下標
        // i:指定開始查找的位置
        // 若是數組爲空,則默認返回-1。不然調用indexOf.call()方法返回其下標
        return arr == null ? -1 : indexOf.call(arr, elem, i);
    },

    // Support: Android <=4.0 only, PhantomJS 1 only
    // push.apply(_, arraylike) throws on ancient WebKit
    // 用於合併兩個數組的元素到第一個數組中,第一個參數能夠是數組或類數組對象,即必須含有整數(或能夠轉換成整些)屬性length;第二個參數能夠數組,類數組對象或任何含有連續屬性的對象。注意:方法jQuery.merge()的合併具備破壞性,將第二個參數合併到第一個參數時,會改變第一個參數。若是不但願改變第一個參數,能夠在調用jQuery.merge()方法前對第一個參數進行備份。
    merge: function (first, second) {
        // 初始化變量,將second.length轉換成數字,並將first.length賦值給i,循環添加second的屬性到first中,由於不肯定first是不是數組,因此用i來修正first.length。最後返回first。
        var len = +second.length,
            j = 0,
            i = first.length;

        for (; j < len; j++) {
            first[i++] = second[j];
        }

        first.length = i;

        return first;
    },

    // 用於查找數組中知足過濾函數的元素,原數組不受影響。
    grep: function (elems, callback, invert) {
        // elems待遍歷查找的數組
        // callback過濾每一個元素的函數,執行時被傳入兩個參數;當前元素和它的下標。該函數應該返回一個布爾值。
        // invert若是參數是false或未傳入,grep()會返回一個知足回調函數的元素數組;若是invert是true,則返回一個不知足回調函數的元素數組。
        var callbackInverse,
            matches = [],
            i = 0,
            length = elems.length,
            callbackExpect = !invert;

        // Go through the array, only saving the items
        // that pass the validator function
        // 遍歷數組elems,爲每一個元素執行過濾函數。若是參數invert爲true,把執行結果爲false的元素放入結果數組matches;若是參數invert爲false,把執行結果爲true的元素放入結果數組matches;
        for (; i < length; i++) {
            callbackInverse = !callback(elems[i], i);
            if (callbackInverse !== callbackExpect) {
                matches.push(elems[i]);
            }
        }

        // 返回結果數組matches
        return matches;
    },

    // arg is for internal usage only
    // 靜態方法jQuery.map()對數組中的每一個元素或對象的每一個屬性調用一個回調函數,並將回調函數的返回值放入一個新數組中。執行回調函數時傳入兩個參數:數組元素或屬性值,元素下標或屬性名。關鍵字this指向全局對象window。回調函數的返回值會被放入新的數組中;若是返回一個數組,數組中將被扁平化後插入結果集;若是返回null或undefined,則不會放入任何元素。
    map: function (elems, callback, arg) {
        // elems:待遍歷的數組或對象
        // callback:回調函數,會在數組的每一個元素或對象的每一個屬性上執行。執行時傳入兩個參數:數組元素或屬性值,元素下標或屬性名。
        // arg:僅限於jQuery內部使用。若是調用jQuery.map()時傳入了參數arg,則該參數會被傳給回調函數callback。
        var length, value,
            i = 0,
            ret = [];

        // Go through the array, translating each of the items to their new values
        // 判斷elems是數組,若是爲true,將經過下標遍歷
        if (isArrayLike(elems)) {
            length = elems.length;
            for (; i < length; i++) {
                // 爲每一個元素執行回調函數callback,執行時依次傳入三個參數:元素、下標、arg
                value = callback(elems[i], i, arg);

                // 若是回調函數的返回值不是null和undefined,則把返回值放入結果集ret
                if (value != null) {
                    ret.push(value);
                }
            }

            // Go through every key on the object,
            // 不然將經過屬性名遍歷
        } else {
            // 對於對象經過for..in循環遍歷屬性名
            for (i in elems) {
                // 爲每一個屬性值執行回調函數callback,執行時依次傳入三個參數:元素、下標、arg
                value = callback(elems[i], i, arg);

                // 若是回調函數的返回值不是null和undefined,則把返回值放入結果集ret
                if (value != null) {
                    ret.push(value);
                }
            }
        }

        // Flatten any nested arrays
        // 最後在空數組[]上調用方法concat()扁平化結果集ret中的元素,並返回。
        return concat.apply([], ret);
    },

    // A global GUID counter for objects
    // guid是一個全局計數器,用於jQuery事件模塊和緩存模塊。在jQuery事件模塊中,每一個事件監聽函數會被設置一個guid屬性,用來惟一標識這個函數。在緩存模塊中,經過在DOM元素上附加一個惟一標識,來關聯元素和該元素對應的緩存。屬性jQuery.guid初始值爲1,使用時自增1。
    guid: 1,

    // jQuery.support is not used in Core but other projects attach their
    // properties to it so it needs to exist.
    // support用於其餘項目附加它們的屬性
    support: support
});

// 添加Symbol屬性
if (typeof Symbol === "function") {
    jQuery.fn[Symbol.iterator] = arr[Symbol.iterator];
    // 爲 arr與jQuery.fn添加Symbol.iterator屬性
    // Symbol.iterator 爲每個對象定義了默認的迭代器。該迭代器能夠被 for...of 循環使用。
}

// Populate the class2type map
// 初始化class2type結果爲:
/*
{
    "[object Boolean]":"boolean",
    "[object Number]":"number",
    "[object String]":"string",
    "[object Function]":"function",
    "[object Array]":"array",
    "[object Date]":"date",
    "[object RegExp]":"regexp",
    "[object Object]":"object",
    "[object Error]":"error",
    "[object Symbol]":"symbol",
} 
*/
jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),
    function (i, name) {
        class2type["[object " + name + "]"] = name.toLowerCase();
    });

    // 用於判斷obj是數組仍是對象
function isArrayLike(obj) {

    // Support: real iOS 8.2 only (not reproducible in simulator)
    // `in` check used to prevent JIT error (gh-2145)
    // hasOwn isn't used here due to false negatives
    // regarding Nodelist length in IE
    //!!obj 用於判斷obj是非空的,即obj不是undefined、null或""(空)。
    var length = !!obj && "length" in obj && obj.length,
        type = toType(obj);

        // 是函數或窗口返回false
    if (isFunction(obj) || isWindow(obj)) {
        return false;
    }

    // elem.length是數值型,知足下列條件則爲true;obj是真正的數組、length等於0、length大於0,且length - 1存在,便是一個類數組對象。
    return type === "array" || length === 0 ||
        typeof length === "number" && length > 0 && (length - 1) in obj;
}
相關文章
相關標籤/搜索