Skip to content

继承

每个构造函数(constructor)都有一个原型对象(prototype),每个实例对像包含一个指向原型对象的指针(__proto__)。

原型链继承

每个构造函数有一个原型对象,原型对象包含一个指向构造函数的指针,而实例包含一个指向原型对象的内部指针。如果我们让原型对象等于另一个类型的实例,那么此时的原型对象就包含了一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。如果我们再更新原型对象,那么就会形成一个原型链。

优点:原型链继承简单易理解,实现起来也相对容易。

缺点:原型链上的所有属性和方法都是共享的,任何一个对象对属性的修改都会反映在其他所有实例上,这种特性在某些情况下可能会造成问题。另外,不能在创建对象时向构造函数传递参数。

js
function SuperType() {
  this.property = true;
}

SuperType.prototype.getSuperValue = function () {
  return this.property;
};

function SubType() {
  this.subproperty = false;
}

// 继承了 SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function () {
  return this.subproperty;
};

let instance = new SubType();
console.log(instance.getSuperValue()); // true

构造函数继承

在子类型构造函数的内部调用超类型构造函数。通过使用 .call().apply() 方法,可以在新创建的对象(存在于子类型构造函数的环境中)上执行超类型构造函数。

优点:可以在子类型构造函数中向超类型构造函数传递参数。

缺点:方法都在构造函数中定义,因此函数复用就无从谈起。而且超类型原型中定义的方法,对子类型而言也是不可见的。

js
function SuperType() {
  this.colors = ["red", "blue", "green"];
}

function SubType() {
  // 继承了 SuperType
  SuperType.call(this);
}

let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"

let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"

组合继承

使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。

优点:既可以实现函数复用,又可以保持每个实例拥有自己的属性。

缺点:调用了两次超类构造函数,生成了两份实例。

js
function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function () {
  console.log(this.name);
};

function SubType(name, age) {
  SuperType.call(this, name); // 继承属性
  this.age = age;
}

SubType.prototype = new SuperType(); // 继承方法

SubType.prototype.constructor = SubType; // 修复构造函数引用
SubType.prototype.sayAge = function () {
  console.log(this.age);
};

let instance1 = new SubType("Bob", 23);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Bob"
instance1.sayAge(); // 23

寄生组合继承

最优,类似 ES6 extends

通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

优点:避免了在原型链和构造函数上面的重复,是 JavaScript 中最理想的继承范式。

js
function inheritPrototype(subType, superType) {
  let prototype = Object.create(superType.prototype); // 创建对象
  prototype.constructor = subType; // 增强对象
  subType.prototype = prototype; // 指定对象
}

function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function () {
  console.log(this.name);
};

function SubType(name, age) {
  SuperType.call(this, name); // 第二次调用 SuperType()
  this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function () {
  console.log(this.age);
};

let instance1 = new SubType("Bob", 23);
instance1.sayName(); // "Bob"
instance1.sayAge(); // 23

hancenter808@outlook.com