__proto__ 是 [[Prototype]] 内部槽的访问器属性,直接读写对象头中的原型指针;Object.getPrototypeOf() 和 setPrototypeOf() 是规范级操作,具备类型安全与环路检测,最终作用于同一内存位置;prototype 是函数的显式属性,与 [[Prototype]] 槽分属不同层级。

从内存物理架构角度理解 __proto__、Object.getPrototypeOf() 和 Object.setPrototypeOf(),关键不在“硬件地址”或“RAM颗粒”,而在于 JavaScript 引擎(如 V8)如何在堆(heap)中组织对象结构、维护隐式引用链,并将这些机制映射到规范定义的抽象层。真正的“底座”,是引擎内部的 [[Prototype]] 内部槽(internal slot)及其在对象头(object header)中的存储方式。
__proto__ 是 [[Prototype]] 的可读写访问接口
__proto__ 不是对象的自有属性,也不是内存中独立分配的一块空间;它是引擎为每个普通对象自动挂载的一个访问器属性(accessor property),其 get 和 set 方法直接读写该对象内部的 [[Prototype]] 槽。
- V8 中,每个 JS 对象在堆上分配时,其对象头(map pointer + properties + elements)之后,会隐式关联一个指向原型对象的指针 —— 这就是
[[Prototype]]槽的物理体现,通常是一个 8 字节(64 位系统)的机器地址。 -
obj.__proto__访问时,引擎不查找属性表,而是直接跳转到对象头的原型指针字段并解引用;赋值时则直接覆写该指针值(需校验目标是否为 object 或 null)。 - 它之所以“非标准但广泛支持”,是因为所有主流引擎都选择用同一套底层机制暴露这个能力,而非各自实现不同命名的访问器。
getPrototypeOf / setPrototypeOf 是规范级的指针操作函数
Object.getPrototypeOf(obj) 和 Object.setPrototypeOf(obj, proto) 并非对 __proto__ 的简单封装,而是直接操作 [[Prototype]] 槽的标准化入口,具有更严格的语义和边界控制:
-
getPrototypeOf对任意值安全:传入原始值(如123)会先装箱为Number对象,再取其[[Prototype]](即Number.prototype);传入null或undefined则直接抛错。 -
setPrototypeOf在写入前执行不可扩展性检查(如果 obj 是 sealed/frozen,操作失败并返回 false)、原型环路检测(防止a.__proto__ = b; b.__proto__ = a),这些逻辑在__proto__赋值中部分引擎也实现,但行为不完全统一。 - 二者在 JIT 编译阶段可能被内联为单条指针读/写指令,性能与
__proto__几乎无差 —— 因为它们最终操作的是同一个内存位置。
prototype 属性与 [[Prototype]] 槽完全不在同一层级
Function.prototype 中的 prototype 是一个**显式属性**,存在于函数对象自身的属性表中;而 [[Prototype]] 是每个对象(包括函数)都有的**内部槽**。两者唯一联系是:当用 new Fn() 创建实例时,引擎会把新对象的 [[Prototype]] 槽设为 Fn.prototype 的内存地址。
-
Fn.prototype本身也是一个普通对象,它也有自己的[[Prototype]]槽(指向Object.prototype),该槽由Fn.prototype.__proto__或Object.getPrototypeOf(Fn.prototype)读取。 - 你不能通过
obj.prototype获取obj的原型 —— 因为只有函数才有prototype属性;obj的原型只能通过[[Prototype]]槽访问,也就是__proto__或getPrototypeOf。 - 这种分离设计让引擎能清晰区分“构造器模板”(
prototype属性)和“运行时继承链”([[Prototype]]槽),内存布局上前者在属性哈希表里,后者在对象头固定偏移处。
为什么 Object.prototype.__proto__ === null?
这是整个原型链在内存中的终止标识。V8 为 Object.prototype 分配对象时,将其 [[Prototype]] 槽显式初始化为 null(即 0x0 地址)。所有属性访问走到这里后,引擎判定链已尽,不再继续查找 —— 这不是 JavaScript 层的约定,而是引擎在 C++ 层对空指针的硬编码判断。
- 这个
null不是全局null值,而是内部表示“无原型”的哨兵值,在内存中就是字面量 0。 - 若强行用
Object.setPrototypeOf(Object.prototype, {})修改它,虽然语法允许,但会破坏引擎内置优化(如 fast-property lookup),且多数现代引擎已禁止此操作并抛出TypeError。

















