
tip:因为本文偏向于底层,希望您已经掌握原型和原型链,new 关键字的作用。并且对二进制有基本概念
我们最常见的类型判断方式有三种,typeof和instanceof还有Object.prototype.toString。
javascript 中有两个类型大纲(基本数据类型和引用数据类型)。
基本数据类型包括有:Number,String,Boolean,null,Undefined,Symbol(ES6)等
引用数据类型包括有:Obejct,Array,Function,Map(ES6),Set(ES6)等
typeof(判断基本数据类型)
typeof 只能用于判断基本数据类型(null 除外)和函数。
在至今为止的所有基本数据类型中,只有 null 会被判断为"obejct",其余都可以准确判断。而在所有的引用数据类型中,只有 Function 会被准确判断,其余均会被判断为 object。
如图 👇

为什么 null 会被判断为 object?
不论我们在编译器中输入的是什么代码,这些代码在最后最后会被编译成二进制数据。而在变量生成的组二进制数据中,系统通过这组二进制的 前三位(从右往左) 来判断当前数据的类型是哪一种。
javascript 类型判断规则 👇
- 000:对象
- 1:整数
- 010:浮点数
- 100:字符串
- 110:布尔
而 null,因为不代表任何数值。他在 javascript 中最初的设计模式的表现形式是:0000000000000000
注意到了吗,null 的前三位,也是 000。
然后 javascript 进行了以下判断 👇(以下是早期的 javascript 源码)
    //1.判断是不是undefined
    if (JSVAL_IS_VOID(v)) {
        type = JSTYPE_VOID;
        //2.判断是不是object
    } else if (JSVAL_IS_OBJECT(v)) {
        obj = JSVAL_TO_OBJECT(v);
        if (obj &&
            (ops = obj -> map -> ops,
                ops == & js_ObjectOps
                    ? (clasp = OBJ_GET_CLASS(cx, obj),
                        clasp -> call || clasp == & js_FunctionClass) // (3,4)
                    : ops -> call != 0)) {  // (3)
            type = JSTYPE_FUNCTION;
        } else {
            type = JSTYPE_OBJECT;
        }
        //3.判断是不是number
    } else if (JSVAL_IS_NUMBER(v)) {
        type = JSTYPE_NUMBER;
        //4.判断是不是string
    } else if (JSVAL_IS_STRING(v)) {
        type = JSTYPE_STRING;
        //5.判断是不是boolean
    } else if (JSVAL_IS_BOOLEAN(v)) {
        type = JSTYPE_BOOLEAN;
    }
当对 null 的这个判断走到 JSVAL_IS_OBJECT(v) 的时候,哦吼,发现符合哎,然后就返回了 object。
所以,到底总结:将 null 判断为 object,是 javascript 在最初的设计模型中的一个漏洞。

instanseof(判断引用数据类型)
instanceof 只能判断当前对象的原型链上,是否存在指定类型,返回 true 或 false。
在 instanceof 的底层原理是,利用 instanceof 左边实例的隐式原型和 instanceof 右边构造函数的显示原型进行循环比对。看看是否有哪一级能对上。有对的上的就返回 true,一直顺着原型链找到头都没有比对成功的,返回 false。
举个例子:
//构造函数
function Parent(lang) {
    this.lang = lang;
}
//实例对象
const p1 = new Parent("月薪一千五,心比美式苦");
p1 instanceof Parent === true;

可能还是没有那么的明白,没事。我们手写一个 instanceof 来解读一下 👇。
//模拟instanceof功能
const myInstanceof = (el, type) => {
    //获取右边构造函数的显示原型
    const typeProto = type.prototype;
    //进行循环查询
    while (true) {
        //如果隐式原型为null,说明已经查找到头了。依旧没找到,说明类型不匹配,返回false。
        if (el.__proto__ === null) return false;
        //如果数据的隐式原型和类型的显示原型重叠,说明类型匹配,返回true
        if (el.__proto__ === typeProto) return true;
        //当前原型链层不为null也没有匹配,则去下一层继续比对
        el = el.__proto__;
    }
};
然后咱们用自己手写的 myInsatnceof 来判断一下类型

完美。
现在你是否已经了解的 instanceof 的原理了?
然后我们聊聊到一个关于 instanceof 在网上最常见的话题,很多小伙伴会笼统的告诉我们:instanceof 只能比对 array 这种引用类型数据。 这句话没错,但是这不代表 instanceof 在就只能判断 array,object 和 set 等类型。
举个例子

哇哦,我比对 Number 居然成功了。

揭晓答案
大家是否记得,new 操作符中有一串代码的作用是将新对象的隐式原型指向构造函数的显示原型。而我们的 instanceof 又是通过原型链去判断类型的。
对,我 new 了一个 Number。将 num1 变成了一个以对象形式存在的数字。

注意,我们直接赋值 javascript 自动推导出来的 number 和 new 出来的 number 不是一个东西。使用 new 构造的数字本质上,是一个对象,而非 number。

所以呀,有时候有人告诉我们不要使用 new 去构造 Number 类型数据。就是因为数据类型会被改变。从而对比对造成一定的问题隐患。
使用 instanceof 比对复杂数据类型存在的弊端
因为 instanceof 是通过原型链去找的,但是所有原型的最顶端,永远都是 Object 类型。所以,就造成了insatnceof 的答案并不具有唯一性。
举个例子 👇

这种问题尤其在我们需要区分当前类型是对象还是其他引用类型数据的时候最麻烦。

像以上这种业务逻辑下,永远都只能进入第一个判断。虽然我们可以通过代码逻辑等手段避免以上情况,但常在河边走,哪有不失足,在有更好的比对方法的情况下,没有必要去承担这种风险。
Object.prototype.toString(全能型选手)
Object.prototype.toString 一定是我们工作中使用的最多的类型判断方法,和 typeof 还有 instanceof 不一样的是,Object.prototype.toString 可以判断任何类型的数据,并且结果具有唯一性。
举个例子 👇

每种类型都有自己唯一的答案。
值得注意的是,Object.prototype.toString 需要配合.call()函数使用。Object.prototype.toString 用于判断返回当前数据的类型,而.call 用于改变 Object.prototype.toString 中的 this 指向。
注意,判断类型的功能只有 Object.prototype.toString 这个函数具有。其他类型上的 toString 都是 Obejct 构造函数的实例,都分别重写了 toString 功能。
栗子 👇

typeof/instanceof/object.prototype.toString 的应用场景
一般的开发中,使用 object.prototype.toString 来判断数据的准确类型一定最稳妥。
typeof 更适合判断当前类型是否为基本数据类型或引用数据类型这种大纲。我曾遇到过一种需求。就是判断数据类型。基本数据类型才给通过。
    //判断当前数据是否是基本数据类型
    if(typeof query !== "object" || query === null)
    //判断当前数据类型是否是引用数据类型
    if(typeof query === "object" || typeof query === "function")
instanceof 这个,emmmm,其实我个人基本不用。
你都看到这里了,不送点封装好的类型判断给你当见面礼,都不好意思。获取封装好的类型判断 上面的文章链接里有我自己使用 object.prototype.toString 封装好的类型判断。
