原型链继承、构造函数继承、组合继承

125 阅读4分钟

继承的概念

父类的属性和方法可以被子类继承,子类可以调用父类的属性和方法

原型链继承(Prototype Inheritance)

原型链继承是 JavaScript 中一种常见的继承方式,它基于原型链的特性。每个对象都有一个原型对象,对象可以通过原型链访问和继承其他对象的属性和方法。当一个对象被用作另一个对象的原型时,后者可以访问前者的属性和方法。这种继承方式并不会盗用或共享父类的全部内容,而是建立一个链接,使子类对象可以在父类的原型链上查找并继承属性和方法。

举例:

// 父类
function Parent() {
  this.name = 'Parent';
}

Parent.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name);
};

// 子类
function Child() {
  this.name = 'Child';
}

Child.prototype = new Parent();

var child = new Child();
child.sayHello(); // 输出: Hello, I am Child

在上述代码中,子类 Child 通过 Child.prototype = new Parent() 实现了原型链继承父类 Parent。因此,当 child 对象调用 sayHello 方法时,它会首先在自身的属性和方法中查找,如果找不到,则会沿着原型链向上查找,最终在父类的原型上找到了 sayHello 方法并执行。

原型链继承是通过建立一个原型链,使子类对象可以在父类的原型链上查找并继承属性和方法。

优点:

  • 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  • 父类新增原型方法和属性,子类都能访问到
  • 简单易于实现

缺点

  • 因为原型对象的属性是共享的,修改原型上的一个属性,其他对象的该属性也变了
  • 创建子类实例时,无法向父类构造函数传递参数

构造函数继承 (Constructor Inheritance)

构造函数继承是通过调用父类的构造函数来创建子类的实例。这样子类可以继承父类构造函数中定义的属性和方法。在这种继承方式下,子类会拥有自己的实例和方法,但无法访问父类构造函数外部定义的方法。

举例:

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

Parent.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name)
}

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

var child = new Child('John')
console.log(child.name) // 输出: John
console.log(child.age)  // 输出: 10

child.sayHello() // 报错:child.sayHello is not a function

在上述代码中,子类 Child 在其构造函数中调用了父类 Parent 的构造函数,并传递了相应的参数。这样,子类实例就可以继承父类构造函数中的属性,如 nameage

构造函数继承是通过调用父类的构造函数(function Parent(){})来创建子类的实例,子类可以继承父类构造函数中定义的属性和方法,但无法访问父类构造函数外部定义的方法。

优点:

  • 解决了子类实例共享父类引用属性的问题
  • 创建子类实例时,可以向父类构造函数传递参数

缺点:

  • 无法实现复用,对每一个子类实例都有一个新的 run 函数,如果实例对象多了,内存消耗过大

组合继承(Combination Inheritance)

组合继承是将原型链继承和构造函数继承结合起来的一种方式。它通过在子类的构造函数中调用父类的构造函数,继承父类的属性,并且使用原型链继承方式继承父类的方法。这种方式既可以拥有独立的实例和方法,也可以访问父类构造函数外部定义的方法。

举例:

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

Parent.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name);
};

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

Child.prototype = new Parent();
Child.prototype.constructor = Child;

var child = new Child('John', 10);
console.log(child.name); // 输出: John
console.log(child.age); // 输出: 10

child.sayHello(); // 输出: Hello, I am John

子类 Child 在其构造函数中使用 Parent.call(this, name) 调用了父类 Parent 的构造函数,并传递了相应的参数,从而实现了继承父类的属性。然后,我们将子类 Child 的原型设置为父类 Parent 的实例,这样子类就可以继承父类原型上的方法。最后,我们还需要将子类 Child 的构造函数指向自身,以确保正确设置构造函数的引用。

组合继承是将原型链继承和构造函数继承结合起来的一种方式。它通过在子类的构造函数中调用父类的构造函数,继承父类的属性,并且使用原型链继承方式继承父类的方法。这种方式既可以拥有独立的实例和方法,也可以访问父类构造函数外部定义的方法。

优点:

  • 不存在引用属性共享的问题
  • 可传递参数,且方法可复用

缺点

  • 子类原则上有一份多余的父类实例的属性

class 的 extends 继承

新手写文,多多包涵,如有错误,欢迎指正