背景
typeof和instanceof操作符都是用来判断数据类型的,但是它们的使用场景却各不相同,其中一些细节也需要特别注意。
typeof
typeof运算符返回一个字符串,表示操作数的类型
使用方法如下:
typeof operand
typeof(operand)
operand表示要返回类型的对象或基本类型的表达式
typeof 666 // 'number'
typeof '666' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {
} // 'object'
从上面例子可以看出,typeof可以精准的判断基本数据类型(null)除外
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
使用方法如下:
object instanceof constructor
object 是指某个实例对象
constructor是指某个构造函数
// 定义构造函数
function C(){
}
function D(){
}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上
C.prototype = {
};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上。
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的 ES 规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 proto 伪属性,是可以实现的。比如执行 obj.proto = {} 之后,obj instanceof Foo 就会返回 false 了。(来源于MDN)
instanceof的实现原理,可以参考下面的代码:
/**
* @description 判断对象是否属于某个构造函数
* @prams left: 实例对象 right: 构造函数
* @return boolean
*/
function myInstanceof(left, right) {
let rightPrototype = right.prototype; // 获取构造函数的显式原型
let leftProto = left.__proto__; // 获取实例对象的隐式原型
while (true) {
// 说明到原型链顶端,还未找到,则返回 false
if (leftProto === null) {
return false;
}
// 隐式原型与显式原型相等
if (leftProto === rightPrototype) {
return true;
}
// 获取隐式原型的隐式原型,重新赋值给 leftProto
leftProto = leftProto.__proto__
}
}
可以简单理解为:顺着原型链去找,直到找到相同的原型对象,返回true,否则为false
区别
typeof与instanceof 都是判断数据类型的方法,区别如下:
- typeof会返回一个运算数的基本类型,instanceof 返回的是布尔值
- instanceof 可以准确判断引用数据类型,但是不能正确判断原始数据类型
- typeof虽然可以判断原始数据类型(null 除外),但是无法判断引用数据类型(function 除外)
扩展
从上面可以看出typeof和instanceof 都存在缺陷,所以如果想要完美的检测数据类型的话,推荐使用Object.prototype.toString.call()方法:
Object.prototype.toString.call({
})
Object.prototype.toString.call([])
Object.prototype.toString.call(666)
Object.prototype.toString.call(true)
Object.prototype.toString.call(null)
Object.prototype.toString.call(undefined)
从结果可以看出Object.prototype.toString.call()方法比typeof和instanceof 都要好;就是写起来比较麻烦。