es6前javascript继承本质是运行时原型链属性委托,通过构造函数prototype和实例__proto__手动构建查找链,实现属性共享与方法复用。

ES6 之前的 JavaScript 继承,本质是对象间通过原型链实现的属性委托,没有类、没有编译期继承关系,只有运行时动态查找。它不靠“定义类结构”,而是靠手动设置对象的 __proto__(或更规范地用 Object.getPrototypeOf())和函数的 prototype 属性来构造一条可向上追溯的链。
构造函数 + prototype 是起点
每个函数(非箭头函数)天生带一个 prototype 属性,它是一个普通对象,默认只有一个 constructor 指回该函数。当你用 new Fn() 创建实例时,JS 引擎会做几件事:
- 创建一个空对象
- 把这个空对象的内部
[[Prototype]]链指向Fn.prototype - 将
this绑定到该对象,执行Fn函数体 - 返回这个对象(除非显式返回其他对象)
也就是说,new 出来的实例,其方法和共享属性都来自 Fn.prototype,而不是拷贝过去——这是复用的基础。
原型链继承:把子类的 prototype 指向父类实例
要让子类型能访问父类型的原型方法,最直接的做法是:
- 让
Child.prototype = new Parent() - 再修正
Child.prototype.constructor指回Child(否则它会指向Parent)
这样,所有 new Child() 得到的实例,其 __proto__ → Child.prototype → Parent 实例 → 它的 __proto__ → Parent.prototype,就串起了一条链。属性查找时,引擎按此链逐级向上找,直到找到或到 null 为止。
借用构造函数继承:解决属性隔离问题
原型链继承有个硬伤:父构造函数里的引用类型属性(如数组、对象)会被所有子实例共享。为避免污染,开发者在子构造函数里用 Parent.call(this, ...args),把父类初始化逻辑“借”过来执行一遍。
- 这确保每个子实例都有自己独立的
this.name、this.hobbies等属性 - 但代价是:父类原型上的方法(比如
sayHi)没被继承过来,子实例无法调用
组合继承:属性 + 方法两全的常用解法
把上面两种方式拼起来,就是 ES5 最主流的继承写法:
- 子构造函数内用
Parent.call(this, ...)继承实例属性 - 子原型用
Object.create(Parent.prototype)(比new Parent()更干净)继承原型方法 - 再手动修复
Child.prototype.constructor
它兼顾了属性独立性和方法复用,但也有小缺陷:父构造函数会在设置原型时被调用一次(产生冗余实例),又在子构造中被调用一次。
底层运行时行为:没有“继承声明”,只有查找路径
JavaScript 引擎从不检查“谁继承了谁”。当你写 child.sayName(),它只做一件事:查 child 自身有没有 sayName;没有,就去 child.__proto__ 找;还没,继续往上,直到 null。这条路径就是原型链,而它的结构完全由你手动赋值 prototype 和 __proto__ 决定——没有魔法,只有明确的对象链接。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











