所属板块:3. 原型与面向对象(OOP)
记录日期:2026-03-xx
更新:遇到继承手写题或原型链继承相关题时补充
1. 继承的演进:从“血泪”到“圣杯”
JS 的继承本质是原型链的委托,没有真正的类。
每一种继承方式都在解决上一种的痛点,但又引入新缺陷,最终才诞生“寄生组合式继承”这个终极方案。
记住核心思路:实例属性用构造函数继承,共享方法用原型继承,两者结合才是王道。
2. 六种继承方式对比(优缺点 + 代码)
-
原型链继承(最简单,但致命缺陷最多)
function Parent() { this.name = "父类"; } Parent.prototype.say = function() { console.log(this.name); }; function Child() {} Child.prototype = new Parent(); // 关键一步 const c = new Child(); c.say(); // "父类"缺点:
- 父类引用类型属性被所有子类共享
- 无法向父类传参
-
盗用构造函数继承(Constructor Stealing)
function Child() { Parent.call(this); // 借用父类构造函数 }优点:能传参,实例属性独立
缺点:无法继承父类原型上的方法 -
组合继承(最常用,但有小浪费)
function Child() { Parent.call(this); // 继承属性 } Child.prototype = new Parent(); // 继承方法缺点:父类构造函数被调用两次(一次 call,一次 new),子类原型上多出一份无用属性
-
原型式继承(Object.create 的底层思想)
function create(proto) { function F() {} F.prototype = proto; return new F(); } -
寄生式继承
在原型式基础上再增强对象(加方法) -
👑 寄生组合式继承(JS 继承的终极圣杯)
- 完美解决组合继承调用两次构造函数的问题
- 目前最推荐的 ES5 继承方式(Vue2 源码、很多框架插件都在用)
3. 寄生组合式继承手写(面试必杀技)
function inherit(Child, Parent) {
// 1. 寄生:用 Object.create 继承原型(不调用 Parent 构造函数)
Child.prototype = Object.create(Parent.prototype);
// 2. 修复 constructor 指向(防止被覆盖)
Child.prototype.constructor = Child;
// 3. 借用构造函数继承实例属性(可传参)
// 在 Child 构造函数里写:Parent.call(this, ...args)
}
// 使用示例
function Parent(name) {
this.name = name;
this.colors = ["red", "blue"];
}
Parent.prototype.say = function() { console.log(this.name); };
function Child(name, age) {
Parent.call(this, name); // 继承属性 + 传参
this.age = age;
}
inherit(Child, Parent); // 关键调用
const c1 = new Child("张三", 18);
const c2 = new Child("李四", 20);
c1.colors.push("green");
console.log(c2.colors); // ["red", "blue"](不共享)
c1.say(); // "张三"
4. 小结 & 复习时的“继承视角”
- 原型链继承 → 盗用构造函数 → 组合继承 → 寄生组合式(一步步优化)
- 核心公式:Object.create(Parent.prototype) + Parent.call(this) + 修复 constructor
- [3-1] 原型三角 + [3-2] new 操作符 + 本文的继承,共同构成了 ES5 面向对象的完整体系
- 遇到“如何实现继承”“为什么子类能调用父类方法”这类题,顺着原型链 + 构造函数 call 去想
下一篇文章会进入 [3-4]:现代面向对象(ES6 class 语法糖本质 + extends/super 底层 + 与 ES5 的差异)——把 class 的“糖衣”彻底撕开。
返回总目录:戳这里