1. 原型链继承
构造函数、原型和实例之间的关系:
- 每个构造函数都有一个原型对象(
protype) 
- 原型对象都包含一个指向构造函数的指针(
constructor) 
- 实例都包含一个指向构造函数原型对象的指针,也叫隐式对象(
__proto__)。 
继承的本质就是复制,即重写原型对象,代之以一个新类型的实例。
1.1. 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | function SuperType() {     this.property = true; }
  SuperType.prototype.getSuperValue = function() {     return this.property; }
  function SubType() {     this.subproperty = false; }
 
  SubType.prototype = new SuperType(); 
  SubType.prototype.getSubValue = function() {     return this.subproperty; }
  const instance = new SubType(); console.log(instance.getSuperValue());  console.log(instance.getSubValue());  console.log(instance instanceof SuperType);  console.log(instance instanceof SubType);  console.log(Object.getPrototypeOf(SubType.prototype) === SuperType.prototype); 
  | 
 

1.2. 优缺点分析
- 优点:父类的方法可以复用
 
- 缺点:多个实例对 引用类型 的操作会被篡改 (原始类型不受影响);子类示例不能给父类构造函数传参
 
1 2 3 4 5 6 7 8 9 10 11 12 13
   | function SuperType() {   this.colors = ['red', 'blue', 'green'] } function SubType() {}
  SubType.prototype = new SuperType()
  const instance1 = new SubType() instance1.colors.push('black') console.log(instance1.colors) 
  const instance2 = new SubType() console.log(instance2.colors) 
  | 
 
2. 借用构造函数继承
使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)
2.1. 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | function SuperType() {   this.color = ['red', 'green', 'blue'] } function SubType() {            SuperType.call(this) } const instance1 = new SubType() instance1.color.push('black') console.log(instance1.color) 
  const instance2 = new SubType() console.log(instance2.color) 
  | 
 
2.2. 优缺点分析
- 优点:父类引用类型的数据不会被子类共享,不会相互影响
 
- 缺点:
- 只能继承父类的实例属性和方法,不能继承父类原型属性/方法
 
- 无法实现复用,每个子类都有父类实例函数的副本,影响性能
 
 
3. 组合继承
组合上述两种方法就是组合继承:
- 用原型链实现对原型属性和方法的继承
 
- 用借用构造函数技术来实现实例属性的继承。
 
3.1. 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | 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) }
  const instance1 = new SubType('Nicholas', 29) instance1.colors.push('black') console.log(instance1.colors)  instance1.sayName()  instance1.sayAge() 
  const instance2 = new SubType('Greg', 27) console.log(instance2.colors)  instance2.sayName()  instance2.sayAge() 
  | 
 

3.2. 优缺点分析
4. 原型式继承
利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。
4.1. 实现
object()对传入其中的对象执行了一次浅复制,将构造函数F的原型直接指向传入的对象。
Object.create() 的方法,能够代替代码中的object方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | function object(obj) {   function F() {}   F.prototype = obj   return new F() } const person = {   name: 'Nicholas',   friends: ['Shelby', 'Court', 'Van'], }
  const anotherPerson = object(person) anotherPerson.name = 'Greg' anotherPerson.friends.push('Rob')
  const yetAnotherPerson = object(person) yetAnotherPerson.name = 'Linda' yetAnotherPerson.friends.push('Barbie')
  console.log(person.friends) 
  | 
 
4.2. 优缺点分析
- 缺点:
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
 
- 无法传递参数
 
 
5. 寄生式继承
在原型式继承的基础上,增强对象,返回构造函数。
5.1. 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | function object(obj) {   function F() {}   F.prototype = obj   return new F() } function createAnother(original) {   const clone = object(original)    clone.sayHi = function () {          console.log('hi')   }   return clone  } const person = {   name: 'Nicholas',   friends: ['Shelby', 'Court', 'Van'], } const anotherPerson = createAnother(person) anotherPerson.sayHi() 
  | 
 
5.2. 优缺点分析
- 缺点(同原型式继承):
- 原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。
 
- 无法传递参数
 
 
6. 寄生组合式继承
结合借用构造函数传递参数和寄生模式实现继承。
目前最优的方案,最成熟的方法,也是现在库实现的方法
6.1. 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
   | function inheritPrototype(subType, superType) {   const 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)   this.age = age }
 
  inheritPrototype(SubType, SuperType)
 
  SubType.prototype.sayAge = function () {   console.log(this.age) }
  const instance1 = new SubType('xyc', 23) const instance2 = new SubType('lxy', 23)
  instance1.colors.push('2')  instance1.colors.push('3')  console.log(instance1.colors) console.log(instance2.colors) instance1.sayName() instance1.sayAge() instance2.sayName() instance2.sayAge()
  | 
 

6.2. 优缺点分析
7. 混入方式继承多个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | function MyClass() {   SuperClass.call(this)   OtherSuperClass.call(this) }
 
  MyClass.prototype = Object.create(SuperClass.prototype)
  Object.assign(MyClass.prototype, OtherSuperClass.prototype)
 
  MyClass.prototype.constructor = MyClass
  MyClass.prototype.myMethod = function () {    }
  | 
 
Object.assign会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。
8. ES6类继承extends
extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中constructor表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError错误,如果没有显式指定构造方法,则会添加默认的 constructor方法。
8.1. 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   | class Rectangle {      constructor(height, width) {     this.height = height     this.width = width   }
       get area() {     return this.calcArea()   }
       calcArea() {     return this.height * this.width   } }
  const rectangle = new Rectangle(10, 20) console.log(rectangle.area)
 
 
  class Square extends Rectangle {   constructor(length) {     super(length, length)
           this.name = 'Square'   }
    get area() {     return this.height * this.width   } }
  const square = new Square(10) console.log(square.area)
 
  | 
 
8.2. 实现原理
extends继承的实现和寄生组合式继承方式一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | function _inherits(subType, superType) {            subType.prototype = Object.create(superType && superType.prototype, {     constructor: {       value: subType,       enumerable: false,       writable: true,       configurable: true,     },   })
    if (superType) {     Object.setPrototypeOf       ? Object.setPrototypeOf(subType, superType)       : (subType.__proto__ = superType)   } }
  | 
 
9. 总结
9.1. 函数声明和类声明的区别
函数声明会提升,类声明不会。
9.2. ES5继承和ES6继承的区别
- ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到
this上( Parent.call(this) ) 
- ES6的继承有所不同,实质上是先创建父类的实例对象
this ,然后再用子类的构造函数修改this 。 因为子类没有自己的this对象,所以必须先调用父类的super() 方法,否则新建实例报错。