JavaScript 中常见的 6 种继承方式


✅ 1. 原型链继承(Prototype Chaining)

原理:

将子类的原型设置为父类的一个实例。这样子类的实例就可以访问父类的属性和方法。

优点:

  • 实现简单。
  • 可以继承父类原型上的方法。

缺点:

  • 父类的引用类型属性会被所有子类实例共享(共享引用问题)。
  • 创建子类实例时,无法向父类构造函数传参。

示例代码:

javascript 复制代码
function Parent() {
    this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayHello = function () {
    console.log("Hello from Parent");
};

function Child() {}
Child.prototype = new Parent(); // 核心:将子类的原型设为父类实例

let child1 = new Child();
child1.colors.push("black");
console.log(child1.colors); // ["red", "blue", "green", "black"]

let child2 = new Child();
console.log(child2.colors); // ["red", "blue", "green", "black"] (共享引用)

✅ 2. 构造函数继承(Constructor Stealing / Classic Inheritance)

原理:

在子类构造函数中调用父类构造函数,使用 call() 或 apply() 改变 this 指向。

优点:

  • 可以在子类构造函数中向父类构造函数传参。
  • 避免了引用类型共享问题。

缺点:

  • 无法继承父类原型上的属性和方法。
  • 方法无法复用,每次创建实例都会重新定义一次方法。

示例代码:

javascript 复制代码
function Parent(name) {
    this.name = name;
    this.colors = ["red", "blue"];
}

function Child(name, age) {
    Parent.call(this, name); // 核心:调用父类构造函数
    this.age = age;
}

let child1 = new Child("Alice", 10);
child1.colors.push("green");
console.log(child1.colors); // ["red", "blue", "green"]

let child2 = new Child("Bob", 12);
console.log(child2.colors); // ["red", "blue"]

✅ 3. 组合继承(Combination Inheritance)

原理:

结合原型链继承和构造函数继承,使用原型链继承原型上的方法,使用构造函数继承实例属性。

优点:

  • 能继承父类原型上的方法。
  • 避免引用类型共享。
  • 可以传参。

缺点:

  • 父类构造函数被调用两次(一次在创建子类原型,一次在子类构造函数中)。

示例代码:

javascript 复制代码
function Parent(name) {
    this.name = name;
    this.colors = ["red", "blue"];
}
Parent.prototype.sayName = function () {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 构造函数继承
    this.age = age;
}
Child.prototype = new Parent(); // 原型链继承
Child.prototype.constructor = Child; // 修正构造函数指向

let child1 = new Child("Alice", 10);
child1.colors.push("green");
console.log(child1.colors); // ["red", "blue", "green"]

let child2 = new Child("Bob", 12);
console.log(child2.colors); // ["red", "blue"]

✅ 4. 原型式继承(Prototypal Inheritance)

原理:

借助一个空对象作为中介,将某个对象作为原型创建新对象。

优点:

  • 简单,适合不创建构造函数的情况下实现对象继承。

缺点:

  • 引用类型会被共享。
  • 不适合需要传参的场景。

示例代码(ES5 Object.create):

javascript 复制代码
let parent = {
    colors: ["red", "blue"],
    sayHello: function () {
        console.log("Hello");
    }
};

let child1 = Object.create(parent);
child1.colors.push("green");

let child2 = Object.create(parent);
console.log(child2.colors); // ["red", "blue", "green"]

✅ 5. 寄生组合式继承(Parasitic Combination Inheritance)

原理:

组合继承的优化版本,避免父类构造函数被调用两次。使用 Object.create() 创建父类原型的副本作为子类原型。

优点:

  • 高效。
  • 避免了构造函数重复调用。
  • 完整继承了父类的实例属性和原型方法。

示例代码:

javascript 复制代码
function Parent(name) {
    this.name = name;
    this.colors = ["red", "blue"];
}
Parent.prototype.sayName = function () {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 构造函数继承
    this.age = age;
}

// 核心:使用 Object.create 创建父类原型的副本
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

let child = new Child("Alice", 10);
child.sayName(); // Alice

✅ 6. ES6 类继承(Class Inheritance)

原理:

ES6 引入了 class 和 extends 关键字,底层仍然是原型链继承的语法糖。

优点:

  • 语法更清晰,接近传统面向对象语言。
  • 支持 super、静态方法、getter/setter 等。

示例代码:

javascript 复制代码
class Parent {
    constructor(name) {
        this.name = name;
        this.colors = ["red", "blue"];
    }

    sayName() {
        console.log(this.name);
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name); // 调用父类构造函数
        this.age = age;
    }

    sayAge() {
        console.log(this.age);
    }
}

let child = new Child("Alice", 10);
child.colors.push("green");
console.log(child.colors); // ["red", "blue", "green"]

let child2 = new Child("Bob", 12);
console.log(child2.colors); // ["red", "blue"]

📌 总结对比表

继承方式 是否继承原型方法 是否共享引用 是否可传参 是否调用两次构造函数 是否推荐
原型链继承 ✅ ❌ ❌ ❌ ❌
构造函数继承 ❌ ✅ ✅ ❌ ❌
组合继承 ✅ ✅ ✅ ✅ ⚠️
原型式继承 ✅ ❌ ❌ ❌ ❌
寄生组合式继承 ✅ ✅ ✅ ❌ ✅
ES6 类继承 ✅ ✅ ✅ ❌ ✅

✅ 推荐使用

  • 现代开发(ES6+) :使用 ES6 类继承 (class + extends),语法清晰,功能强大。
  • ES5 环境 :推荐使用 寄生组合式继承,性能最好,继承最完整。

如果你正在学习 JavaScript 面向对象编程,理解这些继承方式是非常有帮助的,它能帮助你写出更高效、可维护的代码。需要我为你出一个继承方式的对比图或练习题,也可以继续问我!

相关推荐
anew___1 小时前
《从零手写操作系统 (20):ELF加载器——让OS读懂现代编译器》
java·服务器·前端
ss2731 小时前
AI全栈实战 | 2.3-01 组件库实战:Element Plus 怎么搭出专业界面,过度封装为什么成维护负担
前端·javascript·elementui·vue
对空六课1 小时前
动态按钮ID变化时的稳定埋点标识设计
前端·数据分析
福兮说1 小时前
URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错
开发语言·前端·javascript·node.js·url
吴声子夜歌1 小时前
Nginx应用与运维——Nginx Web服务应用实战(Python网站的搭建)
运维·前端·nginx
小的时候可菜了1 小时前
JS 异步的本质:从单线程到 thenable(一条进化链)
开发语言·前端·javascript
liangshanbo12151 小时前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
web打印社区2 小时前
Windows 网页静默打印设置步骤:客户端、防火墙与联调清单
开发语言·前端·javascript·chrome·pdf·ecmascript
恋猫de小郭2 小时前
KMP 又改了编译流程, Separate Compilation 禁止了 `commonMain` 的依赖穿透
android·前端·flutter
吴声子夜歌2 小时前
Nginx应用与运维——Nginx Web服务应用实战(静态文件服务器的搭建)
运维·前端·nginx