typeof和instanceOf的区别

typeof一般我们是用来校验原始数据类型的
String、Number、Object、function、Symbol
但typeof有一个问题,就是判断出来的类型其实并不精准
比如typeof null == 'object',结果会是true,而null其实并不是Object类型

再来看三个例子

console.log(typeof []); // object
console.log(typeof new RegExp('/A/')); // object
console.log(typeof function(){}); // function

可以看到,除了function,其余均为object,所以说,typeof并不能很好的校验数据类型
因此我们常用的是Object.prototype.toString.call()来判断数据类型的

console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new RegExp('/A/'))); // [object RegExp]
console.log(Object.prototype.toString.call(function(){})); // [object Function]

为什么不能直接使用.toString()来判断而需要用原型上的方法?是因为所有非{}类型的Object,都具有各自的toString()方法,比如数组的toString会返回元素的字符串,下面这篇文章写得挺通俗易懂了,我这里就不再继续展开了
js中通过Object.prototype.toString方法----精确判断对象的类型

使用这种方式,可以正确的识别出数组、正则等。但这种方法又有一个问题,那就是没法识别出自定义类型

class Test {}
let test = new Test();
console.log(Object.prototype.toString.call(test)); // [object Object],无法知道是不是Test这个类的实例

上面可以看出我们是无法得知test是否是Tset的实例(也就是无法判断一个变量是否某个对象的实例),因此我们需要使用instanceof来判断

console.log([] instanceof Array); // true
console.log([] instanceof Object); // true

补充一下,虽然在之前关于new的文章里提及过,但这里再次提及一次
prototype:每个函数都有一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,将函数用作构造函数调用(使用new操作符调用)的时候,新创建的对象会从原型对象上继承属性和方法
_proto_: _proto属性是Object.prototype 一个简单的访问器属性,其中包含了get(获取)和set(设置)的方法,任何一个_proto的存取属性都继承于Object.prototype,但一个访问属性如果不是来源于Object.prototype就不拥有_proto属性,譬如一个元素设置了其他的_proto属性在Object.prototype之前,将会覆盖原有的Object.prototype
Object._proto_

constructor: 在Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个function 的 prototype 属性中。这个constructor 保存了指向 function 的一个引用。

非完整模拟instanceof代码,说到底就是拿实例的proto去匹配原函数的prototype,如果有则证明该变量是该实例构造出来的(这里可以去看之前文章new的实现,里面也有一步,将新对象的proto指向函数的prototype,因此翻过来则可以验证该变量是某个对象实例)

function instanceOf(A, B) {
    B = B.prototype;
    A = A.__proto__;

    while (true) {
        if (A === null) return false;

        if (A === B) return true;

        A = A.__proto__;
    }
}

class A {}
let a = new A();

console.log(a instanceof A); // true
console.log(instanceOf(a, A)); // true

但是这个模拟的方法其实并不健全,因为instanceof对基本类型的值实际是返回false的

let str = 'hello';

console.log(str instanceof String); // false
console.log(instanceOf(str, String)); // true

不管'hello'是 new Object() 或 Object()出来的,上面的instanceof都会为true
但是通过自己实现的instanOf来判断,则基本类型也会为true,因此这里证明需要去查找字面量声明和Object()声明的字符串究竟有什么区别

直接打印str.__proto__也是有值的,因为js里一切皆对象,能力有限,底层真正实现是什么我也不得而知,但对到基本类型值,其实在里面先用typeof判断一下,如果不为Object直接返回false其实也可以实现。(能实现,但不确定底层是否如此,所以这里不下结论)

ES6新增 [Symbol.hasInstance] 方法,该方法等价于instanceof,通过以下代码可以看出结果完全一致

function instanceOf(A, B) {
    return B[Symbol.hasInstance](A);
}

class A {}
let a = new A();

console.log(instanceOf(a, A)); // true
console.log(a instanceof A); // true

let str = 'hello';

console.log(str instanceof String); // false
console.log(instanceOf(str, String)); // false

参考文章:
一篇文章看懂 _proto_ 和 prototype 的关系及区别
JS中constructor属性
JavaScript ES6 Symbol.hasInstance的理解。

你可能感兴趣的:(typeof和instanceOf的区别)