instanceof 背后的算法 && 手搓代码

28 阅读2分钟

在面向对象语言里,我们经常需要回答一个问题: “这个对象到底是什么类型?”
在 JavaScript 中,除了 typeofObject.prototype.toString,另一个非常重要的工具就是instanceof 运算符

本文从几个实战示例出发,带你系统理解:

  • instanceof 的真正含义
  • 它与原型 / 原型链的关系
  • 几种常见继承写法对 instanceof 的影响
  • 如何手写一个简单版的 instanceof

我们天天写:

function Animal() {}
function Person() {}

Person.prototype = new Animal();
const p = new Person();

console.log(p instanceof Person); // true
console.log(p instanceof Animal); // true

但 instanceof 到底在判断什么?

1. 一句话原理

A instanceof B 干的事其实就一句话:

看 B.prototype 在不在 A 的原型链上。

也就是从 A.__proto__ 一路往上找,
只要有一层等于 B.prototype,就算 true
一直找到 null 还没找到,就是 false

2. 用数组感受一下原型链

const arr = [];

console.log(arr.__proto__);              // Array.prototype
console.log(arr.__proto__.__proto__);    // Object.prototype

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

因为 arr 的原型链是:

arr → Array.prototype → Object.prototype → null

链上有谁,

instanceof 就会对谁返回 true

3. 手写一个迷你版 instanceof

把刚才那句话翻译成代码,大概就是这个样子:

function myInstanceof(left, right) {
  if (left == null || (typeof left !== 'object' && typeof left !== 'function')) {
    return false;
  }
  let proto = Object.getPrototypeOf(left);
  const target = right.prototype;

  while (proto) {
    if (proto === target) return true;
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

你可以直接对比原生 

instanceof 看结果是不是一样。

4. 继承写法对 instanceof 的影响(只记这三句)

  • 只用 call/apply 借构造函数
    只是拷贝属性,原型链没变child instanceof Parent 一般是 false
  • Child.prototype = new Parent()
    原型链打通了,instanceof 能正确反映继承,但要记得改回 constructor
  • Child.prototype = Object.create(Parent.prototype)
    不共用同一个原型对象,更推荐,instanceof 结果也和预期一致。

5. 什么时候该用 instanceof?

  • 判断“是不是我自定义的某个类型”:value instanceof MyClass
  • 分辨一堆长得像的对象:猫狗猪都继承 Animal,但你只想特殊处理 Dog
  • 排查继承/原型链写错了没:console.log(obj.__proto__) + instanceof

判断内建类型(比如数组)时,Array.isArray 这类 API 通常更稳一点。

小结
不需要把定义背得特别完整,记住一件事就够了:

instanceof 就是在原型链里查 prototype

你会用这个思路画一条原型链,再手写一遍 myInstanceof