Object.getPrototypeOf和Object.setPrototypeOf是直接观测与修改对象[[Prototype]]内部槽的“手术刀”,它们不制造继承,而是暴露和干预原型链的真实运作机制。

要真正吃透 JavaScript 面向对象的底层机制,关键不是记住 API 用法,而是理解原型链如何真实运作——Object.getPrototypeOf 和 Object.setPrototypeOf 正是直接观测和干预这条链的“手术刀”。它们不制造继承,只暴露和修改对象内部的 [[Prototype]] 内部槽(internal slot),而这个槽,才是 new、class、甚至 instanceof 的真正基石。
getPrototypeOf:看清对象真实的原型归属
它返回对象内部 [[Prototype]] 槽指向的对象,不是构造函数的 prototype 属性,也不是你“以为”的父类——而是运行时实际用于属性查找的那一个。
- 对普通对象,它通常返回
Object.prototype;对数组,返回Array.prototype;对用class创建的实例,返回对应类的prototype对象 -
Object.getPrototypeOf([]) === Array.prototype—— 这说明空数组的原型就是Array.prototype,不是Object.prototype,所以能直接调用push、map - 注意:
obj.constructor.prototype不一定等于Object.getPrototypeOf(obj)。如果构造函数的prototype被改写过,二者就会脱钩,这时getPrototypeOf才反映真实链路
setPrototypeOf:谨慎重写原型链,而非“模拟继承”
它直接修改对象的 [[Prototype]] 槽,效果等同于在创建对象时指定原型(如 Object.create(proto)),但它是运行时动态操作,有性能与语义代价。
- 不要用它给已有实例“打补丁式加功能”,比如
Object.setPrototypeOf(obj, MyMixin)。这会破坏 V8 等引擎的内联缓存(IC),导致后续属性访问变慢 - 它适合做原型委托的显式建模,比如实现一个轻量级的“行为复用”:先定义纯方法对象
Loggable = { log() { console.log(this) } },再用setPrototypeOf让某个对象临时获得该能力 - 不可枚举、不可配置的原型链一旦被修改,会影响
instanceof判断结果。例如let x = {}; Object.setPrototypeOf(x, Date.prototype); x instanceof Date // true—— 它真的骗过了类型检测
对比 new / class / Object.create,看清谁在操控 [[Prototype]]
所有面向对象语法糖,最终都归结为对 [[Prototype]] 的设置。掌握这三者的底层映射,才能跳出语法看本质。
立即学习“Java免费学习笔记(深入)”;
-
new Foo():创建空对象 → 将其[[Prototype]]设为Foo.prototype→ 以该对象为this执行Foo构造函数 -
class C { }:本质仍是函数,C.prototype是默认存在的对象,且C.prototype.constructor === C;实例的[[Prototype]]指向它 -
Object.create(proto):最干净的原型设置方式,它创建的对象,其[[Prototype]]直接等于传入的proto,不执行任何构造逻辑,也没有 constructor 属性污染
为什么不能依赖 __proto__?它只是历史遗留的访问器
obj.__proto__ 是非标准、已被废弃的语法,本质是 Object.getPrototypeOf 和 Object.setPrototypeOf 的 getter/setter 封装,但它会触发额外的代理陷阱、影响优化,且在严格模式下对不可扩展对象赋值会静默失败。
- 现代代码应完全避免
__proto__。要用就用getPrototypeOf查,用setPrototypeOf改 - 某些框架(如早期 Vue)曾用
__proto__动态切换响应式原型,后来全部迁移到defineProperty+ 不可变原型链方案,正是因为__proto__的副作用太不可控 - Chrome 和 Node.js 已明确标记
__proto__为 deprecated,TypeScript 也默认报错
不复杂但容易忽略:JavaScript 的面向对象没有“类继承”,只有“原型委托”。getPrototypeOf 和 setPrototypeOf 就是让你亲手摸到那个委托链的接口。用好它们,不是为了炫技,而是为了在调试、设计架构、或阅读源码(比如 React Fiber 或 Vue reactivity)时,一眼看穿对象行为从何而来。


















