# 深入探索源码库中的数据类型检测

# 引子

JavaScript 的数据类型检测是我们平时开发中经常会遇到的场景,小到基本数据类型大至各种引用数据类型的检测,都是我们需要掌握的知识点。本章会详细讲解jQuery最新版本(3. 4. 1)中,关于数据类型检测公共方法封装的源码。

# jQuery中提供的监测数据类型的api

jQuery中给我们提供的一个方法 $. type()

$. type(12); // => "number"
$. type(/^$/); // => "regexp"

# 理解源码基础

# Object. prototype. hasOwnProperty(prop)

根据MDN解释:

含义: hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
参数: prop
要检测的属性以字符串形式String表示,或者 Symbol
返回值: 用来判断某个对象是否含有指定的属性, 返回值为Boolean
描述: 所有继承了 Object 的对象都会继承hasOwnProperty方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性
备注: 即使属性的值是 null 或 undefined,只要属性存在,hasOwnProperty 依旧会返回 true。

o = new Object(); 
o. propOne = null; 
o. hasOwnProperty('propOne'); // 返回 true
o. propTwo = undefined; 
o. hasOwnProperty('propTwo'); // 返回 true

举例: 遍历一个对象的所有自身属性

下面的例子演示了 hasOwnProperty 方法对待自身属性和继承属性的区别
o = new Object(); 
o. prop = 'exists'; 
o. hasOwnProperty('prop'); // 返回 true
o. hasOwnProperty('toString'); // 返回 false
o. hasOwnProperty('hasOwnProperty'); // 返回 false

# 分析源码**$. type()**的实现原理

原理: Object. prototype. toString. call()

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); //=>Object. toString() =>"function Object() { [native code] }"

"Boolean Number String Function Array Date RegExp Object Error Symbol" . split(" "). forEach(function anonymous(item) {

    class2type["[object " + item + "]"] = item.toLowerCase();

}); 
console. log(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"

function toType(obj) {

    //=>obj may be null / undefined   !! null == undefined
    //=>return "null"/"undefined"
    if (obj == null) {
    	return obj + "";
    }
    // toString.call(obj);  
    // 先把传进来的obj生成一个字符串(例:[object RegExp])去class2type中寻找,如果找到了,返回后面的小写类型(例:boolean)
    return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj;

}

// jQuery. type = toType; 

# 检测是否为函数

原理: typeof obj === "function"

var isFunction = function isFunction(obj) {

    return typeof obj === "function";

}; 

# 检测是否为window对象

原理: window. window===window

var isWindow = function isWindow(obj) {

    // 如果obj不传的话 undefined != null => 返回false
    return obj != null && obj === obj.window;

}; 

# 检测是否为纯粹的对象{}(数组和正则等都不是纯粹的对象)

原理: getPrototypeOf获取当前对象的原型

var isPlainObject = function isPlainObject(obj) {

    // !obj都有可能(!null / !undefined)
    var proto, Ctor;
    if (!obj || toString.call(obj) !== "[object Object]") {
    	return false;
    }
    
    //=>getPrototypeOf获取当前对象的原型
    proto = Object.getPrototypeOf(obj);
    // Objects with no prototype (**Object.create( null )**)
    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;
    return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;

}; 

# 检测是否为空对象

原理: for-in循环遍历对象的可枚举属性

var isEmptyObject = function isEmptyObject(obj) {

    var name;
    for (name in obj) {
    	return false;
    }
    return true;

}; 

# 检测是否为数组或者类数组 beta 重点

原理: 根据数组/类数组的lenght属性特点来判断
步骤:
1、将obj通过类型转换(排除null, undefined等没有length属性的非标准值)取得obj. length
2、调用toType(),取得obj类型,function/window类型直接返回
3、判定条件:array类型,length为Number, arr[length-1]存在等

var isArrayLike = function isArrayLike(obj) {

    // !!obj => (转换成Boolean值类型)
    // 1、先把传进来的obj取反取反,转换成Boolean值(排除掉null,undefined值等)
    // 2、再查看有无length属性,
    //   Function.length:1(形参的个数,默认为1) 
    //   window.length:1
    
    var length = !!obj && "length" in obj && obj.length,
    	type = toType(obj);
    if (isFunction(obj) || isWindow(obj)) {
    	return false;
    }
    
    // 运算符优先级,先算逻辑与&&,再算逻辑或||
    return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj;

}; 

# 如何判断一个对象是null还是undefined

注意:
undefined == null => true
但undefined === null => false

obj 如果不传, return "undefined"
let a = null; 
function toType(obj) {

    if (obj == null) {
        if (obj === null) {
            return obj + "";
        } else {
            return obj + "";
        }
    }

}

toType(a); // => "null"

# 写在最后

  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注
  • 在项目中用到最多的有,检测是否为空对象, 检测是否为数组或者类数组,这两个方法最好能够手写下来,面试中大概率问到, 其中检测是否为纯粹的对象{}作为了解即可