JavaScript实现继承的几种方式

前言

之前在Post not found: JavaScript:原型链和原型对象 JavaScript:原型链和原型对象这篇文章中讲述了原型对象和原型链之间的关系,但我们了解原型和原型链的最终目的其实是为了实现继承。

现在主流的几种继承方式有:

  1. 原型链继承
  2. 借用构造函数继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承

在本篇文章中,会一一的介绍这些继承方式的优缺点。

原型链

原型链

既可以继承构造函数中的属性和方法,也可以继承原型链上的属性和方法,

  • 实例化子类时无法给父类传参。
  • 多个实例对引用类型的操作会被篡改。
function Person() {
  this.name = "张三";
  this.old = 18;
}
function Student() {}

Student.prototype = new Person();
var s = new Student();
console.log(s.name);

借用构造函数

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 无法实现复用,每个子类都有父类实例函数的副本,影响性能
function Person() {
  this.name = "张三";
  this.old = 18;
}
Person.prototype.run = function() {
  console.log(this.name + "在运动");
};
function Student() {
  Person.call(this);
}

var s = new Student();
console.log(s.name);

组合继承

缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。

原型式继承

  • 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
  • 无法传递参数
function object(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);
//"Shelby,Court,Van,Rob,Barbie"
console.log(person.name);

寄生式继承

  • 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
  • 无法传递参数
function object(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
function createAnother(original) {
  var clone = object(original); // 通过调用 object() 函数创建一个新对象
  clone.sayHi = function() {
    // 以某种方式来增强对象
    alert("hi");
  };
  return clone; // 返回这个对象
}
var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

寄生组合式继承

这是最成熟的方法,也是现在库实现的方法

function inheritPrototype(subType, superType) {
  var prototype = Object.create(superType.prototype); // 创建对象,创建父类原型的一个副本
  prototype.constructor = subType; // 增强对象,弥补因重写原型而失去的默认的constructor 属性
  subType.prototype = prototype; // 指定对象,将新创建的对象赋值给子类的原型
}

// 父类初始化实例属性和原型属性
function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
  alert(this.name);
};

// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function SubType(name, age) {
  SuperType.call(this, name);
  this.age = age;
}

// 将父类原型指向子类
inheritPrototype(SubType, SuperType);

// 新增子类原型属性
SubType.prototype.sayAge = function() {
  alert(this.age);
};

var instance1 = new SubType("xyc", 23);
var instance2 = new SubType("lxy", 23);

instance1.colors.push("2"); // ["red", "blue", "green", "2"]
instance2.colors.push("3"); // ["red", "blue", "green", "3"]