__proto__ 是非标准遗产,自诞生起未被es标准采纳,仅因兼容性被浏览器容忍;es6将其划入附录b,明确为非强制实现。

JavaScript 中 __proto__ 并非被“逐步废弃”,而是自诞生起就从未成为标准——它只是因兼容性被容忍的浏览器私有实现,ES6 明确将其划入附录 B(Annex B),定义为“仅限浏览器、非强制实现”的历史特性。迁移不是可选项,而是现代开发的必要实践。
为什么说 __proto__ 本质上是“非标准遗产”
它最早由 Firefox 在 2000 年代初引入,用于直接暴露对象内部的 [[Prototype]] 槽;其他浏览器为兼容旧脚本跟进支持。ECMAScript 5(2009)已提供 Object.getPrototypeOf() 和 Object.setPrototypeOf() 等标准接口,但规范选择保留 __proto__ 仅作向后兼容——这不是认可,而是妥协。
- 它从未出现在 ECMAScript 主标准中,Node.js、Deno、Web Workers 等环境不保证支持,某些配置下会被禁用
- 严格模式下对
obj.__proto__ = ...赋值会直接抛出 TypeError - V8 等引擎对其写操作触发去优化(deoptimization),破坏隐藏类缓存,显著拖慢属性访问性能
- 语义易混淆:
__proto__属于实例对象,prototype属于构造函数,二者层级与用途完全不同
读取原型:统一用 Object.getPrototypeOf()
这是唯一标准化、跨环境一致的读取方式,替代所有 obj.__proto__ 的读取场景。
- 在控制台调试时,用
Object.getPrototypeOf(myArray)替代myArray.__proto__,结果相同且语义清晰 - 判断继承关系时,避免
obj.__proto__ === Array.prototype,改用obj instanceof Array或Array.prototype.isPrototypeOf(obj) - 该方法返回对象的内部 [[Prototype]] 值,不可被篡改,也不受引擎优化策略影响
设置原型:按生命周期分场景选标准方法
不能简单用 Object.setPrototypeOf() 一刀切替换赋值操作,需根据对象是否已存在来决策。
- 创建新对象时指定原型 → 优先用
Object.create(proto, descriptors),开销最小、语义最明确 - 已有对象必须变更原型(极少数场景,如测试模拟)→ 使用
Object.setPrototypeOf(obj, proto),但注意它会使 V8 隐藏类失效,影响后续性能 - 绝不要在循环或高频逻辑中反复调用
setPrototypeOf;若需动态行为,应重构为组合、代理(Proxy)或策略模式 - 对象字面量中的
{ __proto__: proto }是 ES6 正式接纳的语法糖,等价于Object.create(proto),但仅限该上下文,不可用于已有对象
初始化与继承:让原型链从源头自动建立
现代代码应尽量避免运行时手动干预原型,而是依靠语言机制在创建阶段自然形成正确链路。
- 使用
class+extends语法,底层自动绑定子类prototype与父类prototype,无需触碰任何__proto__ - 通过
Reflect.construct()创建实例时,可显式指定原型,比直接操作__proto__更可控、更符合规范 - 模块导出/导入对象时,确保原型信息不依赖运行时赋值,而由
Object.create()或class构造器保障
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











