全面解析 JavaScript 类继承:方式、优缺点与应用场景

199 阅读4分钟

在 JavaScript 中,“继承”指的是让一个对象或类能够复用另一个对象或类的属性和方法。
由于 JavaScript 的面向对象机制是基于 原型(prototype) 而非传统类,因此继承方式丰富多样,从 ES5 时代的手动原型链,到 ES6 之后的 class 语法糖,都有不同的实现手段。

本文将系统介绍 8 种主要继承方式,分析它们的优缺点适用场景,并提供代码示例。


1. class 继承(ES6 extends

class Parent {
  greet() { console.log("Hello from Parent"); }
}
class Child extends Parent {
  greet() { super.greet(); console.log("...and from Child"); }
}
new Child().greet();

优点

  • 语法简洁,接近 Java / C# 等语言。
  • 内置 super 调用父类构造和方法。
  • 支持继承内置对象(Array, Error 等)。
  • 性能接近最佳(底层是寄生组合继承)。

缺点

  • 只是语法糖,本质仍是原型链。
  • 高级场景(动态改继承结构)需理解原型机制。

适用场景

  • 现代前端、Node.js 项目的默认选择。

2. class + 表达式继承(动态继承)

function mixin(base) {
  return class extends base {
    extra() { console.log("extra"); }
  }
}
class Parent {}
class Child extends mixin(Parent) {}
new Child().extra();

优点

  • 运行时动态决定父类。
  • 易于组合多个基类。

缺点

  • 可读性差,调试困难。

适用场景

  • 插件系统、UI 组件动态扩展。

3. 原型链继承

function Parent() { this.colors = ["red", "blue"]; }
Parent.prototype.say = function() { console.log("parent"); };

function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

优点

  • 实现简单。
  • 子类可直接访问父类原型方法。

缺点

  • 属性被所有实例共享(引用类型容易出错)。
  • 无法向父类构造传参。

适用场景

  • 学习原型链原理,已不推荐在生产使用。

4. 借用构造函数继承

function Parent(name) { this.name = name; }
function Child(name) { Parent.call(this, name); }

优点

  • 每个实例的属性独立。
  • 可传参。

缺点

  • 无法继承父类原型方法,方法需重复定义。

适用场景

  • 仅继承属性,不需要父类方法的简单对象。

5. 组合继承

function Parent(name) { this.name = name; }
Parent.prototype.say = function() { console.log(this.name); };

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

优点

  • 继承父类属性与方法。
  • 子类实例独立。

缺点

  • 父类构造函数被调用两次。

适用场景

  • ES5 最常用方式,简单可靠。

6. 寄生组合继承

function inherit(Child, Parent) {
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;
}
function Parent(name) { this.name = name; }
Parent.prototype.say = function() { console.log(this.name); };

function Child(name) {
  Parent.call(this, name);
}
inherit(Child, Parent);

优点

  • 父类构造只调用一次,性能好。
  • 完整继承属性和方法。

缺点

  • 写法比组合继承稍复杂。

适用场景

  • ES5 推荐的最佳继承方案。

7. 原型式继承

let parent = { greet() { console.log("hello"); } };
let child = Object.create(parent);
child.greet();

优点

  • 极简,无需构造函数。
  • 灵活,适合快速克隆对象。

缺点

  • 属性共享,引用类型有风险。
  • 无法传参初始化。

适用场景

  • 创建配置模板、数据对象。

8. 寄生式继承

function createChild(o) {
  let clone = Object.create(o);
  clone.say = function() { console.log("hi"); };
  return clone;
}
let parent = { greet() { console.log("hello"); } };
let child = createChild(parent);
child.say();

优点

  • 在原型式继承基础上增强对象。
  • 灵活度高。

缺点

  • 方法无法复用,浪费内存。

适用场景

  • 一次性对象增强。

9. Mixin 混入

const sayMixin = { say() { console.log("hi"); } };
class Person {}
Object.assign(Person.prototype, sayMixin);

优点

  • 模拟多继承。
  • 灵活扩展功能。

缺点

  • 命名冲突风险。
  • 方法来源分散,不易维护。

适用场景

  • 事件系统、功能扩展。

继承方式对比表

方式优点缺点推荐度场景
class 继承语法简洁,支持 super语法糖,本质是原型链⭐⭐⭐⭐⭐现代项目
class + 表达式动态父类可读性差⭐⭐⭐插件系统
原型链继承简单直观属性共享,不能传参学习原理
借用构造函数属性独立,可传参不继承方法⭐⭐仅需属性
组合继承属性独立+继承方法调用两次父构造⭐⭐⭐⭐ES5 常用
寄生组合继承性能最佳写法稍复杂⭐⭐⭐⭐⭐ES5 推荐
原型式继承极简属性共享⭐⭐对象克隆
寄生式继承灵活增强无法复用方法⭐⭐一次性增强
Mixin多继承效果命名冲突风险⭐⭐⭐功能扩展

结语

  • 如果是 现代项目:优先使用 class extends
  • 如果是 ES5 项目:用 寄生组合继承
  • 如果只是克隆/增强对象:用 Object.createMixin
  • 学习原型链原理时,可以用最简单的原型链继承练习。

理解继承方式,不仅是写代码的技巧,更是掌握 JavaScript 对象模型的关键。