[3-3] 原型与面向对象 · 继承的血泪史 (ES5 Inheritance Patterns)

5 阅读2分钟

所属板块:3. 原型与面向对象(OOP)

记录日期:2026-03-xx
更新:遇到继承手写题或原型链继承相关题时补充

1. 继承的演进:从“血泪”到“圣杯”

JS 的继承本质是原型链的委托,没有真正的类。
每一种继承方式都在解决上一种的痛点,但又引入新缺陷,最终才诞生“寄生组合式继承”这个终极方案。

记住核心思路:实例属性用构造函数继承,共享方法用原型继承,两者结合才是王道。

2. 六种继承方式对比(优缺点 + 代码)

  1. 原型链继承(最简单,但致命缺陷最多)

    function Parent() { this.name = "父类"; }
    Parent.prototype.say = function() { console.log(this.name); };
    
    function Child() {}
    Child.prototype = new Parent();   // 关键一步
    
    const c = new Child();
    c.say();   // "父类"
    

    缺点:

    • 父类引用类型属性被所有子类共享
    • 无法向父类传参
  2. 盗用构造函数继承(Constructor Stealing)

    function Child() {
      Parent.call(this);   // 借用父类构造函数
    }
    

    优点:能传参,实例属性独立
    缺点:无法继承父类原型上的方法

  3. 组合继承(最常用,但有小浪费)

    function Child() {
      Parent.call(this);               // 继承属性
    }
    Child.prototype = new Parent();    // 继承方法
    

    缺点:父类构造函数被调用两次(一次 call,一次 new),子类原型上多出一份无用属性

  4. 原型式继承(Object.create 的底层思想)

    function create(proto) {
      function F() {}
      F.prototype = proto;
      return new F();
    }
    
  5. 寄生式继承
    在原型式基础上再增强对象(加方法)

  6. 👑 寄生组合式继承(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 的“糖衣”彻底撕开。

返回总目录:戳这里