「前端进阶」深入探索源码库中的数据类型检测

317 阅读4分钟

引子

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;
};

检测是否为数组或者类数组

原理: 根据数组/类数组的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"

写在最后

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