返回

论JavaScript的五种继承方式

前端

JavaScript作为一门灵活且功能强大的语言,提供了多种继承方式,使得我们可以轻松地创建和扩展对象,复用代码并增强应用程序的可维护性。为了解JavaScript的继承方式,需要先了解JavaScript的原型系统。JavaScript中的每个对象都存在一个称为“原型”的对象,并且每个对象的原型都继承了它的原型,而该原型也继承了它的原型,如此形成一条连续的链条。

原型继承

原型继承是JavaScript中最基本的继承方式,它允许一个对象直接继承另一个对象的属性和方法。

function Parent() {
  this.name = "Parent";
}

Parent.prototype.run = function () {
  console.log(this.name + " is running");
};

function Child() {
  this.name = "Child";
}

// 原型继承
Child.prototype = new Parent();

const c1 = new Child();

c1.run(); // 输出: "Child is running"

在上述代码中,Child类的原型对象直接指向Parent类的实例,这意味着Child类继承了Parent类的所有属性和方法。

构造函数继承

构造函数继承是一种通过在子类构造函数中调用父类构造函数来实现继承的方式。

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

Parent.prototype.run = function () {
  console.log(this.name + " is running");
};

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

// 继承父类原型
Child.prototype = Object.create(Parent.prototype);

const c1 = new Child("Child");

c1.run(); // 输出: "Child is running"

在上述代码中,Child类在自己的构造函数中调用了Parent类的构造函数,并传入了自己的参数,从而实现了对父类属性和方法的继承。

组合继承

组合继承是原型继承和构造函数继承的结合,它既能继承父类的属性和方法,又能调用父类的构造函数。

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

Parent.prototype.run = function () {
  console.log(this.name + " is running");
};

function Child(name) {
  // 调用父类构造函数
  Parent.call(this, name);
  
  // 继承父类原型
  this.__proto__ = Parent.prototype;
}

const c1 = new Child("Child");

c1.run(); // 输出: "Child is running"

在上述代码中,Child类在自己的构造函数中调用了Parent类的构造函数,并传入了自己的参数,从而实现了对父类属性和方法的继承。此外,Child类的原型对象指向Parent类的原型对象,实现了对父类原型的继承。

寄生继承

寄生继承是一种通过创建一个新对象,并将父类对象的属性和方法复制到新对象中来实现继承的方式。

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

Parent.prototype.run = function () {
  console.log(this.name + " is running");
};

function Child(name) {
  // 创建一个新对象
  const child = {};
  
  // 将父类对象的属性和方法复制到新对象中
  Object.assign(child, new Parent(name));
  
  // 额外添加自己的属性和方法
  child.age = 10;
  child.play = function () {
    console.log(this.name + " is playing");
  };
  
  // 返回新对象
  return child;
}

const c1 = Child("Child");

c1.run(); // 输出: "Child is running"
c1.play(); // 输出: "Child is playing"

在上述代码中,Child函数并没有直接继承Parent类,而是创建了一个新对象,并将Parent类的属性和方法复制到新对象中。这样,新对象就具有了Parent类的属性和方法,并且可以额外添加自己的属性和方法。

混入继承

混入继承是一种通过将多个对象的属性和方法合并到一个对象中来实现继承的方式。

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

Parent.prototype.run = function () {
  console.log(this.name + " is running");
};

function Child() {}

// 将Parent对象的属性和方法合并到Child对象的原型中
Object.assign(Child.prototype, Parent.prototype);

// 额外添加自己的属性和方法
Child.prototype.age = 10;
Child.prototype.play = function () {
  console.log(this.name + " is playing");
};

const c1 = new Child();

c1.run(); // 输出: "Child is running"
c1.play(); // 输出: "Child is playing"

在上述代码中,Child类并没有继承Parent类,而是将Parent对象的属性和方法合并到Child对象的原型中。这样,Child对象就具有了Parent对象的属性和方法,并且可以额外添加自己的属性和方法。

结语

JavaScript的五种继承方式各有千秋,在不同的场景下有不同的适用范围。原型继承简单易用,但灵活性较差;构造函数继承灵活度高,但需要手动调用父类构造函数;组合继承既能继承父类的属性和方法,又能调用父类的构造函数,但代码较为复杂;寄生继承简单易用,但不能直接访问父类原型;混入继承可以将多个对象的属性和方法合并到一个对象中,但需要手动合并属性和方法。

在实际开发中,我们可以根据具体情况选择合适的继承方式,以实现最佳的代码结构和性能。