JavaScript typeof和instanceof区别详解

开发过程中,我们经常要判断一个变量的类型,那你知道js中有多少方式判断变量类型吗?

1. typeof

typeof用于基本类型的判断,js有 7 中基本类型,他们分别是:
stringnumberbooleanundefinednullsymbolbigint

它们使用typeof判断的结果如下:

typeof 'hello'; // string
typeof 123;     // number
typeof true;    // boolean
typeof undefined; // undefined
typeof null;    // object
typeof Symbol();  // symbol
typeof Bigint(1); // bigint

这里有个特例,其他的基本类型使用typeof都正确的返回了类型,只有null返回的是object

这是一个错误的结果,但是为了兼容性,js保留了这一现象。

typeof用于引用类型时,结果永远返回object

typeof new Number(1); // object
typeof new Date();    // object

如果想要的判断一个引用类型的变量类型,就需要使用instanceof

2. instanceof

instanceof用于引用类型的判断,比如:

new Number(1) instanceof Number; // true
new Boolean(true) instanceof Boolean; // true;
new Date() instanceof Date; // true
/\d/ instanceof RegExp; // true
({}) instanceof Object; // true
[] instanceof Array; // true

instanceof的原理是原型链,手动实现instanceof的代码可能像下面这样:

function instanceOf(L, R) {//L 表示左表达式,R 表示右表达式 
    var O = R.prototype;   // 取 R 的显示原型 
    L = L.__proto__;  // 取 L 的隐式原型
    while (true) {    
        if (L === null)      
             return false;   
        if (O === L)  // 当 O 显式原型 严格等于  L隐式原型 时,返回true
             return true;   
        L = L.__proto__;  
    }
}

因为原型可以被修改,所以instanceof判断并不一定可靠。

let obj = {};
Object.setPrototypeOf(obj, Array.prototype);
obj instanceof Array; // true

上面的代码使用Object.setPrototypeOf把对象的原型修改成了数组的原型,就产生了对象是Array实例的奇怪现象。

3. Array.isArray

前面提到instanceof可能因为对象原型被修改而产生奇怪的现象,对于数组类型的判断,要紧的做法是使用Array.isArray

Array.isArray({__proto__: Array.prototype}); // false

因为Array.isArray不依赖原型链,因此能避免原型被修改引起的误判。

Array.isArray还能检测iframes:

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]

// Correctly checking for Array
Array.isArray(arr);  // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false

你可能会疑问,为什么不同的iframe中instanceof会失效。因为frames其实是window的实例,而所有 JavaScript 全局对象、函数以及变量均自动成为 window对象的成员。

Array其实也是window的成员:

window.Array === Array; // true

那么你可能会意识到,如果有两个window对象,它们的Array属性相同吗?答案是不相同。上面的代码改造一下就可以验证:

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
xArray === Array; // false
xArray.__proto__ === Array.prototype; // false

Array不同,原型也不同,当然instanceof的结果就是false了。

用一句比喻,就是你不是平行世界中那个老爸生的,所以你不是他的儿子。

instanceof也不可靠,什么可靠?

4.Object.prototype.toString.call

先来看看这句话的含义,就是用obj作为this来调用Object.prototype.toString方法。
这里你要问了,对象不都是自动从Object.prototype继承toString,不能直接调用obj.toString吗?

这就是关键,因为toString可能被子类重写。

'hello'.toString(); // Hello
new Date().toString(); // Mon Mar 14 2022 16:52:58 GMT+0800 (中国标准时间)
new RegExp(/\d/).toString(); // /\d/

上面这几种类型都重写了toString,想要调用Object.prototype.toString,就要用到call方法(或者apply)。

这就是我们的目的,因为Object.prototype.toString不像String.prototype.toStringDate.prototype.toString会对变量进行格式化输出,而是统一的输出'[object 类type…]',type就是值的类型,我们正好借助此特性来判断变量类型。

Object.prototype.toString.call(undefined);        // [object Undefined]
Object.prototype.toString.call(null);             // [object Null]
Object.prototype.toString.call('hello');          // [object String]
Object.prototype.toString.call(true);             // [object Boolean]
Object.prototype.toString.call(123);              // [object Number]
Object.prototype.toString.call(Symbol());         // [object Symbol]
Object.prototype.toString.call(BigInt(1));        // [object BigInt]
Object.prototype.toString.call(new RegExp(/\d/)); // [object RegExp]
Object.prototype.toString.call(new Date());       // [object Date]

代替instanceof

Object.prototype.toString.call(new Date()) === '[object Date]'; // true

可以看到,这种方式基本类型和引用类型都能用,如果你好奇它的原理,可以看我的另一篇文章。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值