__proto__ 是非标准的历史遗留属性,不推荐使用;应改用标准的 object.getprototypeof() 和 object.setprototypeof()。前者安全获取原型,后者显式设原型并报错而非静默失败,兼容性好且语义清晰。

直接说结论:`__proto__` 是历史产物,有兼容性但不推荐;`Object.getPrototypeOf()` 和 `Object.setPrototypeOf()` 才是标准、安全、可维护的现代写法。
__proto__ 是什么,为什么它“沧桑”
`__proto__` 是早期浏览器(如 Firefox、Chrome 初期)为方便调试和快速原型操作而暴露的非标准属性。它直接挂载在每个对象上,指向其内部 `[[Prototype]]`,看起来像一个普通属性,用起来也简单:
- 读取:`obj.__proto__` 可以拿到原型对象
- 设置:`obj.__proto__ = otherObj` 能强行改原型
- 问题在于:它不是 ECMAScript 标准,只是事实上的“惯例”,V8 等引擎后来虽保留支持,但明确标记为“deprecated in favor of standard methods”
- 更麻烦的是:它在严格模式下对不可扩展对象赋值会静默失败;某些环境(如部分 Web Worker 或旧版 IE)行为不一致;还容易与 `prototype` 属性混淆(后者只属于函数)
getPrototypeOf 和 setPrototypeOf 是怎么接管的
ES5 正式引入 `Object.getPrototypeOf(obj)` 作为获取原型的标准方式,ES6 进一步补全 `Object.setPrototypeOf(obj, proto)` 作为设置原型的唯一推荐途径。它们操作的是对象的内部 `[[Prototype]]` 插槽,不依赖任何属性名,语义清晰、行为稳定:
-
获取原型:
Object.getPrototypeOf(obj)返回 `obj` 的 `[[Prototype]]`,返回值一定是对象或null,不会出错也不会被污染 -
设置原型:
Object.setPrototypeOf(obj, proto)显式设定原型,失败时抛出TypeError(比如目标对象不可扩展且新原型不匹配),便于及时发现逻辑错误 - 兼容性很好:现代所有主流引擎(包括 Node.js 12+、Chrome 30+、Firefox 30+、Safari 9+)都完整支持,比 `__proto__` 更可靠
实际迁移中要注意的关键细节
把旧代码换成新 API 不是简单替换字符串,得理解背后约束:
- 不可扩展对象不能改原型:如果 `Object.isExtensible(obj) === false`,`setPrototypeOf` 一定失败——这是保护机制,不是 bug
- 避免链式污染:`setPrototypeOf` 会改变整个原型链的起点,慎用于已参与继承体系的对象(如类实例),否则可能破坏 `instanceof` 或方法查找
- 替代 `__proto__` 的写法要统一:不要混用,比如一边用 `obj.__proto__ = x`,一边用 `Object.getPrototypeOf(obj)`,这会让团队协作和代码审查变困难
- 创建新对象优先用 `Object.create()`:比起先建空对象再设原型,`Object.create(proto, descriptors)` 更简洁、意图更明确
一个干净的现代写法示例
假设你想让 rabbit 继承 animal,并后续切换原型:
const animal = { eats: true };
const rabbit = Object.create(animal); // 创建时就指定原型
// ✅ 正确读取
console.log(Object.getPrototypeOf(rabbit) === animal); // true
// ✅ 安全修改
Object.setPrototypeOf(rabbit, { jumps: true });
console.log(rabbit.jumps); // true
console.log(rabbit.eats); // undefined(原链断开)
// ❌ 不要再这样写
// rabbit.__proto__ = { runs: true };











