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>