算法 - 手写 instanceof

1. 题目

实现一个函数 myInstanceof(left, right),等价于原生 instanceof 运算符:

  • 判断 left 是否是 right 的实例,即 right.prototype 是否出现在 left原型链上。
  • 是返回 true,否则返回 false

示例:

1
2
3
4
myInstanceof([], Array)      // true
myInstanceof([], Object) // true(Array.prototype 的原型是 Object.prototype)
myInstanceof(1, Number) // false(原始值不在原型链上;new Number(1) 则 true)
myInstanceof(null, Object) // false

2. 解题思路

instanceof 的本质:沿 left 的原型链逐级向上查找,看能否碰到 right.prototype

关键概念澄清:

  • instanceof 判断的不是「谁 new 出来的」,而是「left.__proto__ 链上有没有 right.prototype 这个对象引用」。
  • 原型链靠 __proto__(即 Object.getPrototypeOf)向上走;构造函数的 prototype 挂在实例的 __proto__ 上。

算法步骤:

  1. left 是原始值或 null,直接 false(原始值没有原型链,原生也会返回 false)。
  2. right.prototype 作为目标。
  3. Object.getPrototypeOf(left) 开始,沿 __proto__ 上溯:命中目标即 true;到 null(原型链顶)仍未命中则 false

进阶:原生 instanceof 会先调用 right 上的静态方法 Symbol.hasInstance(若定义),能提到这点是满分细节。

  • 时间复杂度:O(原型链长度)

3. TypeScript 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function myInstanceof(left: any, right: any): boolean {
// 原始值 / null / undefined 直接 false
if (left === null || (typeof left !== "object" && typeof left !== "function")) {
return false;
}

// 支持 Symbol.hasInstance(对齐原生优先级)
const hasInstance = right[Symbol.hasInstance];
if (typeof hasInstance === "function") {
return !!hasInstance.call(right, left);
}

if (typeof right !== "function") {
throw new TypeError("Right-hand side of 'instanceof' is not callable");
}

const proto = right.prototype; // 要找的目标
let cursor = Object.getPrototypeOf(left);

while (cursor !== null) {
if (cursor === proto) return true;
cursor = Object.getPrototypeOf(cursor); // 沿原型链上溯
}

return false;
}

// 测试
class Animal {}
class Dog extends Animal {}

console.log(myInstanceof(new Dog(), Dog)); // true
console.log(myInstanceof(new Dog(), Animal)); // true(继承链)
console.log(myInstanceof(new Dog(), Object)); // true
console.log(myInstanceof("str", String)); // false(原始值)
console.log(myInstanceof(new String("s"), String)); // true

4. 面试延伸

  • 原型链三条铁律要能脱口而出:实例.__proto__ === 构造函数.prototype构造函数.__proto__ === Function.prototypeObject.prototype.__proto__ === null(链顶)。
  • instanceof vs typeof vs Object.prototype.toStringtypeof 只分原始类型、instanceof 查原型链判引用类型、toString.call(x) 最精确(能区分 Array/Date/RegExp)。三者的适用边界是高频追问。
  • 跨 realm 失效iframe 里的数组用 instanceof Array 会 false(不同全局的 Array.prototype 不同),此时应改用 Array.isArray。能点出这个坑非常加分。
  • class 语法糖extends 建立的正是一条 Dog.prototype.__proto__ === Animal.prototype 的链,instanceof 才因而能向上命中父类。
  • Function instanceof Function === trueObject instanceof Function === true 这些「鸡生蛋」常用来考你原型图是否清晰。

难度:中等 | 手写题 | 原型链理解试金石