likes
comments
collection
share

JavaScript 中的 instanceof

作者站长头像
站长
· 阅读数 8

我曾经对它毫不关心,直到我在一次面试中被问到它。ಠ_ಠ

instanceof 是什么?

instanceof 是 JavaScript 中的一个二元操作符,顾名思义,它用来检测某个对象是否是某个构造函数的实例,或者说,用于检查一个对象是否属于某个特定的 Class。

顺带一提,instanceof 是会考虑继承关系的,也就是说,如果用它来检测一个对象是否是某个 Class 的实例,那么,如果这个对象是这个 Class 的子类的实例,instanceof 也会返回 true。

instanceof 的用法

// 如果 obj 隶属于 Class 类(或 Class 类的衍生类),则返回 true,否则返回 false
obj instanceof Class;

例如:

class Plant {}
class Flower extends Plant {}

class Animal {}
class Dog extends Animal {}

const flower = new Flower();
const dog = new Dog();

console.log(flower instanceof Plant); // true
console.log(flower instanceof Flower); // true
console.log(flower instanceof Animal); // false
console.log(flower instanceof Dog); // false

对了,instanceof 也可以被用于内置对象,例如:

const arr = [];

console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true

instanceof 的实现原理

instanceof 的实现原理大概是这样的:

  • 如果 Class 存在静态方法 Symbol.hasInstance,那么调用它去判断 obj 是否是 Class 的实例,instanceof 的结果就是它的返回值;假如 Symbol.hasInstance 方法的返回值不是 boolean 类型,会被转换成 boolean 类型
  • 如果 Class 不存在静态方法 Symbol.hasInstance,那么就会去判断 obj 的原型链上是否存在 Class.prototype,如果存在,那么 obj 就是 Class 的实例,instanceof 的结果就是 true,否则就是 false

一些有趣的例子

在知道了 instanceof 的实现原理之后,我们可以写一些有趣的例子来验证一下:

这里的 dog 对象不是由 Animal 构造函数创建的,但是,由于 Animal 存在静态方法 Symbol.hasInstance,dog 是否是 Animal 的实例,就取决于 Symbol.hasInstance 方法的返回值了:

class Animal {
  static [Symbol.hasInstance](obj) {
    if (obj.canEat) return true;
  }
}

const dog = { canEat: true };

console.log(dog instanceof Animal); // true

假如我们修改了对象的原型链(通过直接操作 __proto__),可以让 instanceof 返回一些奇怪的结果,比如 dog 原本是 Animal 的实例,改了 __proto__ 之后又不是了:

class Animal {}

const dog = new Animal();

console.log(dog instanceof Animal); // true

dog.__proto__ = null;

console.log(dog instanceof Animal); // false

仿造实现一个 instanceof

ps: 下面的例子中使用了 Object.getPrototypeOf 方法,它实际上就是返回了 __proto__ 属性的内容。至于为什么不直接使用 __proto__ 属性,其实是因为 __proto__ 属性是非标准的,ECMAScript 标准中并没有要求 JavaScript 引擎实现它(尽管事实上它被实现了)。

function myInstanceof(obj, Class) {
  if (typeof Class !== "function") {
    throw new Error("Right-hand side of 'instanceof' is not callable");
  }

  if (typeof obj !== "object" || obj === null) return false;

  let proto = Object.getPrototypeOf(obj);

  while (true) {
    if (proto === null) return false;
    if (proto === Class.prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
}

instanceof 与 typeof 的区别

插播一下 instanceof 与 typeof 的区别。

typeof 用于检测一个值的类型,instanceof 用于检测一个对象是否是某个 Class 的实例,它们的主要区别是:

  1. typeof 返回的是基本类型的名字,instanceof 返回的是一个布尔值
  2. typeof 可以检测基本类型,instanceof 只能检测对象(typeof 对象会得到 object;instanceof 基本数据类型,会得到 false)