JavaScript继承本质是[[Prototype]]链的建立与属性查找,对象通过隐藏槽指向原型,new操作第二步设置该链接,属性访问按需逐级委托,方法共享但this独立,引用类型共享源于原型指向同一堆内存。

JavaScript 传统继承机制没有“底层源码”可直接查看,因为它的核心逻辑由 JavaScript 引擎(如 V8、SpiderMonkey)用 C++ 实现,不向开发者暴露源码级接口。但我们可以基于规范(ECMAScript)和引擎行为,还原其内存模型与关键操作的实质——即:所有继承表现,最终都归结为 对象内部 [[Prototype]] 链的建立与属性查找规则。
原型链的内存结构:每个对象都有一个隐藏链接
每个普通对象在内存中都持有一个内部槽 [[Prototype]],它是一个指向另一个对象(或 null)的引用。这个链接不是属性,不可枚举,也不参与 for...in,但决定属性访问路径。
- 构造函数的
prototype属性,仅用于new操作时设置新实例的[[Prototype]] -
Object.getPrototypeOf(obj)返回的就是 obj 的[[Prototype]] -
obj.__proto__是非标准但广泛支持的访问方式,等价于上者
new 操作符的四步内存动作
执行 const inst = new Parent('Alice') 时,引擎实际完成:
- 分配一块新内存,创建空对象
inst - 将
inst.[[Prototype]]指向Parent.prototype - 以
inst为this,调用Parent('Alice') - 若构造函数无显式返回对象,则自动返回
inst
注意:这四步中,第二步才是继承发生的本质时刻——它把子实例和父原型连进了同一条链。
立即学习“Java免费学习笔记(深入)”;
属性查找:单向、线性、懒执行的委托过程
当访问 inst.sayHi() 时,引擎不会预建完整链表,而是按需逐级查找:
- 先查
inst自身是否有sayHi(自有属性) - 没有 → 查
inst.[[Prototype]](即Parent.prototype) - 仍没有 → 查
Parent.prototype.[[Prototype]](通常是Object.prototype) - 再没有 → 查到
null,返回undefined
整个过程不复制方法,不生成代理,纯靠指针跳转。方法体只存一份,在 Parent.prototype 上;所有实例共享同一份代码,但各自维护独立的 this 上下文。
引用类型共享问题的内存根源
问题代码:function Parent() { this.hobbies = ['coding']; } + Child.prototype = new Parent()
-
new Parent()创建一个对象,其hobbies是堆中一个数组对象的引用 - 该对象被赋给
Child.prototype,意味着所有Child实例的[[Prototype]]都指向它 - 当
child1.hobbies.push('gaming')执行时,修改的是堆中那个唯一数组,child2通过原型链读到的仍是同一地址
这不是“设计缺陷”,而是原型委托的自然结果:委托的是对象本身,不是对象的快照。


















