返回

JavaScript 继承的 6 种方式大比拼:优缺点详解

前端

JavaScript 继承:理解六种方法

概要

JavaScript 继承是构建对象层次结构并实现代码重用性的基本机制。通过继承,子类可以访问和修改父类中的属性和方法。了解 JavaScript 中可用的不同继承方式对于编写可扩展且可维护的代码至关重要。

六种 JavaScript 继承方式

1. 原型继承

原型继承是 JavaScript 中最简单的继承形式。子类的原型对象指向父类的实例,使子类可以访问父类原型中的属性和方法。

优点:

  • 节省内存,因为子类不会重复存储父类属性。
  • 保留对父类构造函数的引用。

缺点:

  • 修改子类原型会影响所有子类实例。
  • 无法继承父类构造函数中的属性和方法。

示例:

function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);

2. 构造函数继承

构造函数继承涉及在子类的构造函数中调用父类的构造函数。这使子类可以继承父类的属性和方法,包括构造函数中定义的属性和方法。

优点:

  • 可以继承父类构造函数中的属性和方法。
  • 每个子类实例都拥有自己属性副本。

缺点:

  • 冗余代码,因为每个子类都必须重新实现父类方法。
  • 消耗更多内存,因为每个子类实例都存储着重复的属性。

示例:

function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

3. Object.create()

Object.create() 方法允许使用现有对象作为原型来创建新对象。这提供了创建新对象的灵活性,并允许多重继承。

优点:

  • 灵活创建新对象,无需构造函数。
  • 支持多重继承。

缺点:

  • 无法继承父类构造函数中的属性和方法。
  • 调试困难,因为对象之间的关系不明显。

示例:

const animal = {
  name: 'Animal'
};

const dog = Object.create(animal);
dog.breed = 'German Shepherd';

4. 类继承(ES5)

ES5 中的类继承使用 extends 来创建子类。子类继承父类原型和静态方法。

优点:

  • 语法简洁,与其他语言类似。
  • 支持多重继承。

缺点:

  • 受 ES5 标准限制,无法继承构造函数中的属性和方法。
  • 性能略低于其他继承方式。

示例:

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

5. ES6 class 关键词

ES6 中的 class 关键词本质上与 ES5 类继承相同,但语法更现代。它支持构造函数继承和私有属性。

优点:

  • 语法更简洁,与 ES6 特性高度集成。
  • 支持构造函数继承和私有属性。

缺点:

  • 仍然受 ES5 标准的限制,无法继承构造函数中的属性和方法。
  • 性能与 ES5 类继承类似。

示例:

class Animal {
  #name;

  constructor(name) {
    this.#name = name;
  }

  get name() {
    return this.#name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

6. 组合继承

组合继承结合了多种继承方式的优点,创建了一个复合继承模型。它通常用于继承父类构造函数中的属性和方法。

优点:

  • 灵活结合不同继承方式的优点。
  • 可以继承父类构造函数中的属性和方法。

缺点:

  • 语法复杂,调试困难。
  • 内存消耗较大,需要存储多个对象引用。

示例:

function Animal(name) {
  this.name = name;
}

function Dog(name, breed) {
  // 原型继承
  Dog.prototype = Object.create(Animal.prototype);

  // 构造函数继承
  Dog.call(this, name);

  // 附加属性
  this.breed = breed;
}

选择最佳继承方式

选择最合适的 JavaScript 继承方式取决于具体需求:

  • 轻量级继承:原型继承
  • 重度继承:构造函数继承
  • 多重继承:Object.create()
  • 简洁语法:类继承(ES5)或 ES6 class 关键词
  • 复合继承:组合继承

常见问题解答

1. 哪个继承方式性能最佳?

构造函数继承通常比其他继承方式性能更好。

2. 如何确定子类是否继承了父类的原型属性?

可以使用 Object.getPrototypeOf() 方法来检查子类的原型对象是否指向父类的原型对象。

3. 如何在 ES6 中实现多重继承?

使用 class 关键词无法直接实现多重继承,但可以通过使用 mixin 或代理对象等技术变相实现。

4. 如何防止子类修改父类原型?

可以使用 Object.freeze() 方法冻结父类原型,以防止子类修改。

5. 组合继承有什么缺点?

组合继承的缺点在于语法复杂,调试困难,并且内存消耗较大。