typeof与instancof在进行类型判断时的区别
如果熟悉JavaScript的程序员应该对这两个判断函数不陌生,但是这两个在本质上还是有一定的区别。
typeof的实现原理
typeof其实最被广大程序员们使用,typeof可以用来判断number,string,undefined,null,boolean,object,symbol这七种数据类型。在typeof进行对基本数据类型的判断时,它能准确的告诉我们属于哪种数据类型。
关于typeof的实现原理,其实在JavaScript底层存储变量的时候,会在变量的机器码的低位1~3位存储其类型信息
- 000:对象
- 010:浮点数
- 100:字符串
- 110:布尔
- 1:整数
null:所有机器码均为0
undefined:用 −2^30 整数来表示
所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待。
instanceof的实现原理
关于instanceof可以自己手动实现一个,它主要是通过原型链一直找到这个需要判断的对象的原型,直到找到为止就返回true,否则就返回false。
以下是我自己实现的instanceof:
// 手动实现一个instanceof
function myInstanceof(obj,type) {
// 如果是基本数据类型就返回false
if (typeof obj !== 'object' || obj === null) {
return false
}
// getPrototypeOf是object对象自带的函数,可以获取到参数的原型对象
let proto = Object.getPrototypeOf(obj)
while (true) {
// 如果查找到底部还没有找到相应的原型对象,则返回false
if (proto == null) {
return false
}
if (proto == type.prototype) {
// 找到相同的原型对象后
return true
}
}
}
但是对于instanceof和typeof的使用场景,当如果是基本数据类型就使用typeof进行判断,如果是对一个对象需要判断它的数据类型就需要使用instanof,例如数组和除 Function 外的所有构造函数的类型都是 ‘object’
例如:
var str = new String('String');
var num = new Number(100);
typeof str; // 返回 'object'
typeof num; // 返回 'object'
var func = new Function();
typeof func; // 返回 'function'
这种时候就可以使用instanceof来判断这个对象为什么类型。
以下是typeof使用时返回值的全列,拷贝自MDN:
// 数值
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值
typeof 42n === 'bigint';
// 字符串
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean()
// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// 对象
typeof {a: 1} === 'object';
// 使用 Array.isArray 或者 Object.prototype.toString.call
// 区分数组和普通对象
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof /regex/ === 'object'; // 历史结果请参阅正则表达式部分
// 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
// 函数
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';
- 值得注意的是即使对NaN进行判断,依然返回的是一个Number。
- 对正则表达式进行判断返回的是一个对象。
- 对一个数组进行判断返回的是一个对象
第三种判断数据类型的方法
还有一个不错的判断类型的方法,就是Object.prototype.toString,我们可以利用这个方法来对一个变量的类型来进行比较准确的判断,在MDN上有这么一句话:Early JS environments return ‘[object Object]’ for null, so it’s best to directly check for it.后面接的就是前面对象的数据类型。
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('hi') // "[object String]"
Object.prototype.toString.call({a:'hi'}) // "[object Object]"
Object.prototype.toString.call([1,'a']) // "[object Array]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(() => {}) // "[object Function]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
总结
当需要对一个对象进行具体数据类型判断的时候就可以使用instanceof,但是instanceof的判断是不准确的,可以使用typeof来判断基本数据类型,不过要注意判断null类型的问题。如果想要获取准确的判断对象实例的数据类型,可以采用Object.prototype.toString.call 方法。