# 继承

# 1️⃣ 原型链继承

function Parent () {
  this.name = 'Daren'
  this.skills = ['Driving', 'Cooking']
}

Parent.prototype.getName = function () {
  return this.name
}

function Child () {}

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

var ming = new Child()
ming.name = 'Ming'
ming.skills.push('Shopping')
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]

var tian = new Child()
tian.name = 'Tian'
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking", "Shopping"]

存在的问题:

  1. 父类引用类型属性被所有子类实例共享
  2. 在创建子类实例时,不能像父类传参

# 2️⃣ 借用构造函数(经典继承)

function Parent (name) {
  this.name = name
  this.skills = ['Driving', 'Cooking']
  this.getName = function () {
    return this.name
  }
}

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

var ming = new Child('Ming')
ming.skills.push('Shopping')
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]

var tian = new Child('Tian')
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking"]

优点:

  1. 避免父类应用类型被子类实例共享
  2. 在创建子实例时,可以向父类传参

缺点:方法都在父类构造函数中定义,每次创建子类实例都会创建一遍方法。

# 3️⃣ 组合继承(原型链继承+经典继承)

function Parent (name) {
  this.name = name
  this.skills = ['Driving', 'Cooking']
}

Parent.prototype.getName = function () {
  return this.name
}

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

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

var ming = new Child('Ming', 24)
ming.skills.push('Shopping')

console.log(ming.name, ming.age) // "Ming" 24
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]

var tian = new Child('Tian', 28) 
console.log(tian.name, tian.age) // "Tian" 24
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking"]

# 4️⃣ 原型式继承

function createInstance(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

var person = {
  name: 'Ming',
  skills: ['Driving', 'Cooking']
}

var ming = createInstance(person)
ming.skills.push('Shopping')
console.log(ming.name) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]


var tian = createInstance(person)
tian.name = 'Tian'
console.log(tian.name) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking", "Shopping"]

缺点:如果父类包含引用类型的属性值则会被子类共享,这点与原型链继承一样的。

# 5️⃣ 寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

function createInstance (o) {
    var clone = Object.create(o);
    clone.getName = function () {
        return this.name
    }
    return clone;
}

var person = {
  name: 'Ming',
  skills: ['Driving', 'Cooking']
}

var ming = createInstance(person)
ming.skills.push('Shopping')
console.log(ming.name) // "Ming"
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]

var tian = createInstance(person)
tian.name = 'Tian'
console.log(tian.name) // "Tian"
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking", "Shopping"]

# 6️⃣ 寄生组合式继承

function Parent (name) {
    this.name = name;
    this.skills = ['Driving', 'Cooking'];
}

Parent.prototype.getName = function () {
    return this.name
}

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

var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();

Child.prototype.constructor = Child

var ming = new Child('Ming', 24)
ming.skills.push('Shopping')
console.log(ming.name, ming.age) // "Ming" 24
console.log(ming.getName()) // "Ming"
console.log(ming.skills) // ["Driving", "Cooking", "Shopping"]

var tian = new Child('Tian', 25)
console.log(tian.name, tian.age) // "Tian" 25
console.log(tian.getName()) // "Tian"
console.log(tian.skills) // ["Driving", "Cooking"]

封装一下:

function createInstance(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function extend(child, parent) {
    var prototype = createInstance(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

// 当我们使用的时候:
extend(Child, Parent);

function Parent (name) {
    this.name = name;
    this.skills = ['Driving', 'Cooking'];
}

Parent.prototype.getName = function () {
    return this.name
}

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

这种方式的高效率体现它只调用了一次父类构造函数,并且因此避免了在 父类原型 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变,因此,能够正常使用 instanceofisPrototypeOf。所以寄生组合式继承是引用类型最理想的继承范式。

# 参考