有一点需要留意,
arr
同时还隶属于
Object
类。因为从原型上来讲,
Array
是继承自
Object
的。
通常,
instanceof
在检查中会将原型链考虑在内。此外,我们还可以在静态方法
Symbol.hasInstance
中设置自定义逻辑。
obj instanceof Class
算法的执行过程大致如下:
如果这儿有静态方法
Symbol.hasInstance
,那就直接调用这个方法:
大多数 class 没有
Symbol.hasInstance
。在这种情况下,标准的逻辑是:使用
obj instanceOf Class
检查
Class.prototype
是否等于
obj
的原型链中的原型之一。
换句话说就是,一个接一个地比较:
obj.__proto__ === Class.prototype?
obj.__proto__.__proto__ === Class.prototype?
obj.__proto__.__proto__.__proto__ === Class.prototype?
// 如果任意一个的答案为 true,则返回 true
// 否则,如果我们已经检查到了原型链的尾端,则返回 false
在上面那个例子中,
rabbit.__proto__ === Rabbit.prototype
,所以立即就给出了结果。
而在继承的例子中,匹配将在第二步进行:
这里还要提到一个方法
objA.isPrototypeOf(objB)
,如果
objA
处在
objB
的原型链中,则返回
true
。所以,可以将
obj instanceof Class
检查改为
Class.prototype.isPrototypeOf(obj)
。
这很有趣,但是
Class
的 constructor 自身是不参与检查的!检查过程只和原型链以及
Class.prototype
有关。
创建对象后,如果更改
prototype
属性,可能会导致有趣的结果。
就像这样:
这是通过
toString
方法实现的。但是这儿有一个隐藏的功能,该功能可以使
toString
实际上比这更强大。我们可以将其作为
typeof
的增强版或者
instanceof
的替代方法来使用。
听起来挺不可思议?那是自然,精彩马上揭晓。
按照
规范
所讲,内建的
toString
方法可以被从对象中提取出来,并在任何其他值的上下文中执行。其结果取决于该值。
对于 number 类型,结果是
[object Number]
对于 boolean 类型,结果是
[object Boolean]
对于
null
:
[object Null]
对于
undefined
:
[object Undefined]
对于数组:
[object Array]
……等(可自定义)
让我们演示一下:
// 特定于环境的对象和类的 toStringTag:
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
正如我们所看到的,输出结果恰好是
Symbol.toStringTag
(如果存在),只不过被包裹进了
[object ...]
里。
这样一来,我们手头上就有了个“磕了药似的 typeof”,不仅能检查原始数据类型,而且适用于内建对象,更可贵的是还支持自定义。
所以,如果我们想要获取内建对象的类型,并希望把该信息以字符串的形式返回,而不只是检查类型的话,我们可以用
{}.toString.call
替代
instanceof
。
让我们总结一下我们知道的类型检查方法:
正如我们所看到的,从技术上讲,
{}.toString
是一种“更高级的”
typeof
。
当我们使用类的层次结构(hierarchy),并想要对该类进行检查,同时还要考虑继承时,这种场景下
instanceof
操作符确实很出色。