jquery源碼中檢測數據類型的方法學習

jquery源碼中的數據類型檢測使用:
通過學習,可以自己封裝類,常用的方法,是否是空對象isEmptyObject,是否是數組或類數組isArrayLike,檢測數據類型toType

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jQuery源碼中的檢測數據類型</title>
</head>

<body>
    <!-- 先在本地下載jquery,然後引入,就可使用 -->
    <script src="./node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        // console.log($.type([])); //array
        // jquery源碼中的Type方法,檢測數據類型
        var class2type = {};
        var toString = class2type.toString; //=>即Object.prototype.toString
        var hasOwn = class2type.hasOwnProperty; //=>Object.prototype.hasOwnProperty原型上的方法
        var fnToString = hasOwn.toString; //=>Function.prototype.toString ->轉換爲字符串的
        var ObjectFunctionString = fnToString.call(Object);//=>讓Function.prototype.toString.call(Object)執行,this是Object,即"function Object() { [native code] }"
        // Populate the class2type map
        // 常見的數據類型的類組成的字符串,以空格進行拆分變成一個數組,然後循環數組中的每一項
        // each寫法:
        // jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),
        //     function (i, name) {
        //         class2type["[object " + name + "]"] = name.toLowerCase();
        //     });
        // forEach寫法:
        "Boolean Number String Function Array Date RegExp Object Error Symbol".split(" ").forEach(function anonymous(item) {
            class2type["[object " + item + "]"] = item.toLocaleLowerCase();
        })
        // console.log(class2type);//{[object Boolean]: "boolean", [object Number]: "number", [object String]: "string", [object Function]: "function", [object Array]: "array", …}[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"__proto__: Object
        function toType(obj) {
            // obj 可能 null / undefined 
            // => return "null" / "undefined"
            if (obj == null) {
                return obj + "";
            }

            // Support: Android <=2.3 only (functionish RegExp)
            // 檢測是什麼類型的值,是引用類型的值還是基本類型
            return typeof obj === "object" || typeof obj === "function" ?
                //如果是引用類型走這個 
                class2type[toString.call(obj)]  //將toString.call(obj)->就是Object.prototype.toString.call(obj)得到的值與class2type中的每一項進行匹配,是其中的某項就返回"[object 某一類的名稱]"所對應符合的小寫類名,如果是"[object Array]",就返回"array"
                ||
                "object" //若沒有在class2type中看到符合的,就直接返回"object"
                :
                //如果是基本類型的值,走這個
                typeof obj;
        }
        // jQuery.type = toType; //在源碼中有這個賦值,使用jquery.type的時候,調用的就是這個toType方法
        // 綜上所述的jquery的數據類型檢測分爲三大類:
        //  =>1.null和undefined,直接返回:"null","undefined"
        //  =>2.基本類型,直接返回typeof的結果,"String","Boolean","Number","Symbol"
        //  =>3.是先將Object.prototype.toString的結果,就是:"[object 某個值]",與之前生成的class2type做對比,能對比上的,就返回它對應的某個值;一個都對不不上的直接返回:"object"
        // 是否爲函數
        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.
            return typeof obj === "function" && typeof obj.nodeType !== "number";
        };
        // 檢測是否爲window對象
        // =>檢測原理window.window===window
        var isWindow = function isWindow(obj) {
            return obj != null && obj === obj.window;
        };

        // 是否爲純粹的對象{},數組和正則等都不是純粹的對象
        var isPlainObject = function (obj) {
            var proto, Ctor;
            // Detect obvious negatives
            // Use toString instead of jQuery.type to catch host objects
            if (!obj || toString.call(obj) !== "[object Object]") { //如果obj沒傳,或者檢測數據類型的toString的值不是"[object Object]"就不是純粹的對象,因爲"{}"對象永遠爲真
                return false;
            }
            // 過濾自定義類的實例
            // => getPrototypeOf 獲取當前對象的原型
            proto = Object.getPrototypeOf(obj);
            /*
                let obj1 = {name:"OBJ1"};
                let obj2 = Object.create(obj1); //創建一個空對象,並且把obj1作爲空對象的原型鏈
                console.log(obj2); //它的原型是obj1
                console.log(Object.getPrototypeOf(obj2)); //obj1
            */
            // proto = getProto(obj);

            // Objects with no prototype (e.g., `Object.create( null )`) are plain
            if (!proto) {
                return true;
            }

            // Objects with prototype are plain iff they were constructed by a global Object function
            /*
            Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
              中hasOwn.call(proto, "constructor")=>相當於
                proto.hasOwnProperty("constructor");
                相當於Object.prototype.hasOwnproperty.call(proto),constuctor是不是當前原型上的私有屬性
                看原型上是否有constructor屬性,有的話獲取這個屬性,沒有的話返回false
            */
            Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
            return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
            // 有constructor,且Ctor是個對象,所以肯定是對象
        }
        // 是否是空對象          ---常用
        var isEmptyObject = function (obj) {
            var name;
            // 如過可以循環,就不是空對象,返回false,反之返回true
            for (name in obj) {
                return false;
            }
            return true;
        }
        // 是否爲數組或者類數組  ---常用
        var isArrayLike = 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
            // 定義個變量length,!!obj將obj轉換爲布爾類型,如果傳的不是null或undefined,就是存在,就是true,
            // => 再驗證傳的值中有沒有length屬性
            // => 有length就獲得這個length,賦值給length
            var length = !!obj && "length" in obj && obj.length,
                type = toType(obj); //檢測obj的類型
            // 函數,有length屬性,代表參數的個數;而window也有length,window.length=0; 如果是兩者中的一個就不是數組或者類數組
            if (isFunction(obj) || isWindow(obj)) {
                return false;
            }
            // 運算符&&優先於||,滿足以下三個條件之一的就是數組或類數組
            //  =>類型是array
            //  =>或者 有length屬性,length===0,可能是空數組(空的類數組)
            //  =>或者有length屬性,並且length是大於0,length長度-1,就是最大的索引,這個索引是obj的屬性(in是檢測是否是它的屬性)
            return type === "array" || length === 0 ||
                typeof length === "number" && length > 0 && (length - 1) in obj;
        }
    </script>
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章