__proto__ 是定义在 object.prototype 上的不可枚举访问器属性,本质是调用 object.getprototypeof() 和 object.setprototypeof() 的 getter/setter,而非对象自有属性。

__proto__ 看起来像对象自身的属性,但其实它根本不在对象上,而是定义在 Object.prototype 上的一个访问器属性(accessor property),本质是一对 getter 和 setter 描述符。
__proto__ 不是对象“拥有”的属性
当你写 obj.__proto__,JavaScript 并不是在 obj 自身或它的原型链上“查找一个叫 __proto__ 的数据属性”,而是在执行 Object.prototype.__proto__ 这个访问器的 getter 方法。这个方法内部调用 Object.getPrototypeOf(obj),并把 this 绑定为当前对象(即 obj)。
- 所有普通对象(包括数组、函数、{} 等)都继承自
Object.prototype,所以都能访问到这个 getter/setter - 你无法通过
Object.getOwnPropertyNames(obj)或for...in枚举出__proto__,因为它被定义为不可枚举(enumerable: false) - 即使手动在
obj上设置obj.__proto__ = ...,也只是触发 setter,而非覆盖或新增自有属性
它的底层实现接近这样
规范虽未强制要求具体实现,但主流引擎(如 V8)的行为与以下伪代码高度一致:
Object.defineProperty(Object.prototype, "__proto__", {
get() {
return Object.getPrototypeOf(this);
},
set(value) {
Object.setPrototypeOf(this, value);
},
enumerable: false,
configurable: true
});
- getter 中的
this指向调用时的对象(比如p.__proto__中的p) - setter 接收新原型对象,并委托给标准方法
Object.setPrototypeOf()执行(该操作有性能代价) - 正因为它是定义在
Object.prototype上的,所以连Function.prototype、Array.prototype等也共享这一行为
为什么强调它是“存取描述符”而非普通属性
因为它的值不存储在对象内部,每次读写都是动态计算或干预原型链:
- 读取时:不返回某个内存地址里的固定值,而是实时查询内部 [[Prototype]] 内部槽
- 写入时:不是简单赋值,而是触发原型重绑定,可能引发引擎优化回退(“去优化”)
- 它只是 [[Prototype]] 这个内部属性对外暴露的操作接口,就像
Object.keys()是对内部键集合的封装一样
实际开发中该注意什么
既然它是访问器,就该按访问器的方式对待:
- 避免在循环或高频路径中反复读取
obj.__proto__,可缓存Object.getPrototypeOf(obj)结果 - 不要把它当成普通字段来 delete 或 defineProperty 覆盖——它由引擎控制,强行覆盖可能失效或报错
- 需要判断原型关系时,优先用
Object.getPrototypeOf()和Object.setPrototypeOf(),语义更清晰、兼容性更好











