instanceof 底层调用右侧构造函数的 symbol.hasinstance 方法,结合 object.getprototypeof 可实现协议式、非侵入式的多态类型检查。

直接用 instanceof 判断自定义类型时,底层实际调用了右侧构造函数的 [Symbol.hasInstance] 方法。而 Object.getPrototypeOf 能精准获取对象的原型,二者结合可绕过构造函数绑定,实现更灵活、可组合、非侵入式的多态类型检查逻辑。
理解 Symbol.hasInstance 的执行机制
obj instanceof Constructor 等价于调用 Constructor[Symbol.hasInstance](obj)。该方法默认行为是沿 obj 的原型链向上查找,看是否等于 Constructor.prototype。但你可以完全重写它,不依赖 Constructor.prototype,甚至不依赖构造函数本身。
- 只要返回
true或false,JS 就接受这个判断结果 - 方法接收的是被检测对象(
obj),不是原型或类名,所以你可以在其中自由读取其属性、原型、Symbol、甚至调用方法 - 它不会自动触发原型链遍历——那是默认实现做的事;你重写后,遍历逻辑由你控制
用 Object.getPrototypeOf 替代 instanceof 的原型查找
传统 instanceof 隐式依赖原型链,但有时你想“跳过中间层”或“按协议而非构造器”判断。这时可用 Object.getPrototypeOf(obj) 显式拿到当前原型,再手动比对或委托给其他判定逻辑。
- 例如:检测一个对象是否“表现得像 Promise”,不关心它是不是
Promise构造出来的,只看它的原型是否具有then方法 - 再如:多个类共享同一语义角色(如
Drawable),但无继承关系。可在它们的原型上统一设置一个标记 Symbol,然后在[Symbol.hasInstance]中用Object.getPrototypeOf(obj)取出原型,检查该 Symbol 是否存在
组合实现协议式多态检查
定义一个“类型检查器”对象,它不作为构造函数,只提供 [Symbol.hasInstance],内部用 Object.getPrototypeOf + 多重条件判断:
const Drawable = {
[Symbol.hasInstance](obj) {
const proto = Object.getPrototypeOf(obj);
// 检查原型是否有 draw 方法
if (typeof proto.draw === 'function') return true;
// 或检查是否实现了某个私有协议 Symbol
if (proto[Symbol.for('is-drawable')] === true) return true;
// 或检查是否继承自某已知 Drawable 子类原型
return proto === Circle.prototype || proto === Rect.prototype;
}
};
之后即可自然使用:myCircle instanceof Drawable —— 语义清晰,不污染实例,支持动态扩展。
注意陷阱与兼容性细节
Symbol.hasInstance 是可配置的,但仅在函数对象上生效(普通对象设了也不会被 instanceof 调用)。若想让任意对象参与该协议,需将其包装为函数(哪怕空函数):
- 错误写法:
({})[Symbol.hasInstance] = () => true;→ 不起作用 - 正确写法:
const Drawable = function(){}; Drawable[Symbol.hasInstance] = ...; -
Object.getPrototypeOf(null)或undefined会抛错,务必先做存在性判断 - V8 和 SpiderMonkey 支持良好,但旧版 Safari(≤14.1)对非函数对象上的
Symbol.hasInstance行为不一致,建议始终绑定到函数











