在 JavaScript 中,"继承"指的是让一个对象或类能够复用另一个对象或类的属性和方法。
由于 JavaScript 的面向对象机制是基于 原型(prototype) 而非传统类,因此继承方式丰富多样,从 ES5 时代的手动原型链,到 ES6 之后的 class
语法糖,都有不同的实现手段。
本文将系统介绍 8 种主要继承方式 ,分析它们的优缺点 与适用场景,并提供代码示例。
1. class 继承(ES6 extends
)
js
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 + 表达式继承(动态继承)
js
function mixin(base) {
return class extends base {
extra() { console.log("extra"); }
}
}
class Parent {}
class Child extends mixin(Parent) {}
new Child().extra();
优点
- 运行时动态决定父类。
- 易于组合多个基类。
缺点
- 可读性差,调试困难。
适用场景
- 插件系统、UI 组件动态扩展。
3. 原型链继承
js
function Parent() { this.colors = ["red", "blue"]; }
Parent.prototype.say = function() { console.log("parent"); };
function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
优点
- 实现简单。
- 子类可直接访问父类原型方法。
缺点
- 属性被所有实例共享(引用类型容易出错)。
- 无法向父类构造传参。
适用场景
- 学习原型链原理,已不推荐在生产使用。
4. 借用构造函数继承
js
function Parent(name) { this.name = name; }
function Child(name) { Parent.call(this, name); }
优点
- 每个实例的属性独立。
- 可传参。
缺点
- 无法继承父类原型方法,方法需重复定义。
适用场景
- 仅继承属性,不需要父类方法的简单对象。
5. 组合继承
js
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. 寄生组合继承
js
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. 原型式继承
js
let parent = { greet() { console.log("hello"); } };
let child = Object.create(parent);
child.greet();
优点
- 极简,无需构造函数。
- 灵活,适合快速克隆对象。
缺点
- 属性共享,引用类型有风险。
- 无法传参初始化。
适用场景
- 创建配置模板、数据对象。
8. 寄生式继承
js
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 混入
js
const sayMixin = { say() { console.log("hi"); } };
class Person {}
Object.assign(Person.prototype, sayMixin);
优点
- 模拟多继承。
- 灵活扩展功能。
缺点
- 命名冲突风险。
- 方法来源分散,不易维护。
适用场景
- 事件系统、功能扩展。
继承方式对比表
方式 | 优点 | 缺点 | 推荐度 | 场景 |
---|---|---|---|---|
class 继承 | 语法简洁,支持 super | 语法糖,本质是原型链 | ⭐⭐⭐⭐⭐ | 现代项目 |
class + 表达式 | 动态父类 | 可读性差 | ⭐⭐⭐ | 插件系统 |
原型链继承 | 简单直观 | 属性共享,不能传参 | ⭐ | 学习原理 |
借用构造函数 | 属性独立,可传参 | 不继承方法 | ⭐⭐ | 仅需属性 |
组合继承 | 属性独立+继承方法 | 调用两次父构造 | ⭐⭐⭐⭐ | ES5 常用 |
寄生组合继承 | 性能最佳 | 写法稍复杂 | ⭐⭐⭐⭐⭐ | ES5 推荐 |
原型式继承 | 极简 | 属性共享 | ⭐⭐ | 对象克隆 |
寄生式继承 | 灵活增强 | 无法复用方法 | ⭐⭐ | 一次性增强 |
Mixin | 多继承效果 | 命名冲突风险 | ⭐⭐⭐ | 功能扩展 |
结语
- 如果是 现代项目 :优先使用
class extends
。 - 如果是 ES5 项目 :用 寄生组合继承。
- 如果只是克隆/增强对象:用 Object.create 或 Mixin。
- 学习原型链原理时,可以用最简单的原型链继承练习。
理解继承方式,不仅是写代码的技巧,更是掌握 JavaScript 对象模型的关键。