instanceof
运算符允许检查对象是否属于某个类。它还考虑了继承。
在许多情况下可能需要进行这样的检查。例如,它可用于构建多态函数,该函数根据参数的类型对参数进行不同的处理。
instanceof 运算符
语法为
obj instanceof Class
如果 obj
属于 Class
或继承自 Class
的类,则它返回 true
。
例如
class Rabbit {}
let rabbit = new Rabbit();
// is it an object of Rabbit class?
alert( rabbit instanceof Rabbit ); // true
它还适用于构造函数
// instead of class
function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
…以及内置类,如 Array
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
请注意,arr
也属于 Object
类。这是因为 Array
在原型上继承自 Object
。
通常,instanceof
会检查原型链以进行检查。我们还可以在静态方法 Symbol.hasInstance
中设置自定义逻辑。
obj instanceof Class
算法大致如下
-
如果有静态方法
Symbol.hasInstance
,则直接调用它:Class[Symbol.hasInstance](obj)
。它应该返回true
或false
,我们完成。这就是我们可以自定义instanceof
行为的方式。例如
// setup instanceOf check that assumes that // anything with canEat property is an animal class Animal { static [Symbol.hasInstance](obj) { if (obj.canEat) return true; } } let obj = { canEat: true }; alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called
-
大多数类没有
Symbol.hasInstance
。在这种情况下,使用标准逻辑:obj instanceOf Class
检查Class.prototype
是否等于obj
原型链中的某个原型。换句话说,逐个比较
obj.__proto__ === Class.prototype? obj.__proto__.__proto__ === Class.prototype? obj.__proto__.__proto__.__proto__ === Class.prototype? ... // if any answer is true, return true // otherwise, if we reached the end of the chain, return false
在上面的示例中,
rabbit.__proto__ === Rabbit.prototype
,所以立即给出了答案。在继承的情况下,匹配将在第二步进行
class Animal {} class Rabbit extends Animal {} let rabbit = new Rabbit(); alert(rabbit instanceof Animal); // true // rabbit.__proto__ === Animal.prototype (no match) // rabbit.__proto__.__proto__ === Animal.prototype (match!)
以下是 rabbit instanceof Animal
与 Animal.prototype
比较的说明
顺便说一下,还有一个方法 objA.isPrototypeOf(objB),如果 objA
在 objB
的原型链中的某个位置,则返回 true
。因此,obj instanceof Class
的测试可以改写为 Class.prototype.isPrototypeOf(obj)
。
有趣的是,Class
构造函数本身不参与检查!只有原型链和 Class.prototype
才重要。
当在创建对象后更改 prototype
属性时,可能会导致有趣的后果。
就像这里
function Rabbit() {}
let rabbit = new Rabbit();
// changed the prototype
Rabbit.prototype = {};
// ...not a rabbit any more!
alert( rabbit instanceof Rabbit ); // false
奖励:Object.prototype.toString 用于类型
我们已经知道普通对象被转换为字符串为 [object Object]
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // the same
这是它们对 toString
的实现。但是有一个隐藏的功能,使得 toString
实际上比这强大得多。我们可以将它用作扩展的 typeof
和 instanceof
的替代品。
听起来很奇怪?确实如此。让我们揭开谜底。
根据 规范,可以从对象中提取内置 toString
,并在任何其他值的环境中执行。其结果取决于该值。
- 对于数字,它将是
[object Number]
- 对于布尔值,它将是
[object Boolean]
- 对于
null
:[object Null]
- 对于
undefined
:[object Undefined]
- 对于数组:
[object Array]
- …等等(可自定义)。
我们来演示一下
// copy toString method into a variable for convenience
let objectToString = Object.prototype.toString;
// what type is this?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
这里我们使用了call,如装饰器和转发,call/apply一章中所述,在上下文this=arr
中执行函数objectToString
。
在内部,toString
算法检查this
并返回相应的结果。更多示例
let s = Object.prototype.toString;
alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]
Symbol.toStringTag
可以使用特殊对象属性Symbol.toStringTag
自定义 Object toString
的行为。
例如
let user = {
[Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
对于大多数环境特定的对象,都有这样的属性。以下是一些浏览器特定的示例
// toStringTag for the environment-specific object and class:
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
。
总结
让我们总结一下我们所知道的类型检查方法
适用于 | 返回 | |
---|---|---|
typeof |
基本类型 | 字符串 |
{}.toString |
基本类型、内置对象、具有Symbol.toStringTag 的对象 |
字符串 |
instanceof |
对象 | true/false |
正如我们所看到的,{}.toString
在技术上是一个“更高级”的typeof
。
当我们使用类层次结构并希望检查考虑继承的类时,instanceof
运算符真正发挥了作用。
评论
<code>
标记,对于多行,请用<pre>
标记将其包装起来,对于 10 行以上的内容,请使用沙箱 (plnkr、jsbin、codepen…)