JavaScript 继承实现方式

原型链继承

  • 将子类的原型对象指向父类的实例
  • 优点:继承了父类的模板,又继承了父类的原型对象
  • 缺点:
    • 无法实现多继承(因为已经指定了原型对象了)
    • 父类的所有 引用属性(info)会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
    • 创建子类时,无法向父类构造函数传参数
1function Parent() { 2 this.info = { 3 name: 'Parent', 4 age: 18, 5 } 6} 7 8Parent.prototype.getInfo = function () { 9 console.log(this.info) 10} 11 12function Child() {} 13 14// 将子类的原型对象指向父类的实例 15Child.prototype = new Parent() 16 17let child = new Child() 18child.info.gender = 'M' 19child.getInfo() // { name: 'Parent', age: 18, gender: 'M' }

构造函数继承

  • 在子类构造函数内部使用 applycall 来调用父类构造函数,复制父类的实例属性给子类
  • 优点:
    • 解决了原型链继承中子类实例共享父类引用对象的问题,实现多继承
    • 创建子类实例时,可以向父类传递参数
  • 缺点:
    • 构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法(方法属性写在构造函数中,每次创建示例都会被初始化)
1function Parent(name) { 2 this.info = { 3 name, 4 hobby: ['football', 'basketball'], 5 } 6} 7 8Parent.prototype.getInfo = function () { 9 console.log(this.info) 10} 11 12function Child(name, age) { 13 // 继承父类属性 14 Parent.call(this, name) 15 this.age = age 16} 17 18// 继承父类方法 19Child.prototype = new Parent() 20 21let child1 = new Child('wujie1', 19) 22child1.info.hobby.push('soccer') 23console.log(child1.getInfo()) // { name: 'wujie1', hobby: [ 'football', 'basketball', 'soccer' ] } 24console.log(child1.age) 25 26let child2 = new Child('wujie2', 20) 27console.log(child2.getInfo()) // { name: 'wujie2', hobby: [ 'football', 'basketball' ] } 28console.log(child2.age)

组合继承

  • 使用原型链继承保证子类继承父类原型的属性和方法
  • 使用构造继承保证子类继承父类实例的属性和方法
1function Parent(name) { 2 this.info = { 3 name, 4 hobby: ['football', 'basketball'], 5 } 6} 7 8Parent.prototype.getInfo = function () { 9 console.log(this.info) 10} 11 12function Child(name, age) { 13 // 继承父类属性 14 Parent.call(this, name) 15 this.age = age 16} 17 18// 继承父类方法 19Child.prototype = new Parent() 20 21let child1 = new Child('wujie1', 19) 22child1.info.hobby.push('soccer') 23console.log(child1.getInfo()) // { name: 'wujie1', hobby: [ 'football', 'basketball', 'soccer' ] } 24console.log(child1.age) 25 26let child2 = new Child('wujie2', 20) 27console.log(child2.getInfo()) // { name: 'wujie2', hobby: [ 'football', 'basketball' ] } 28console.log(child2.age)

原型式继承

  • 通过拷贝对象引用方式实现,但可能导致对象被修改
1let parent = { 2 name: 'parent', 3 hobby: ['football', 'basketball'], 4} 5 6let child = Object.create(parent) 7child.name = 'child' 8child.hobby.push('soccer') 9 10console.log(child.name) // child 11console.log(child.hobby) // [ 'football', 'basketball', 'soccer' ]

寄生式继承

  • 通过获取对象的浅拷贝,再对浅拷贝方法增强(添加方法),也就是在原型式寄生的基础上再添加方法
1let parent = { 2 name: 'parent', 3 hobby: ['football', 'basketball'], 4} 5 6function clone(original) { 7 let clone = Object.create(original) 8 clone.getHobby = function () { 9 return this.hobby 10 } 11 return clone 12} 13 14let child = clone(parent) 15child.name = 'child' 16child.hobby.push('soccer') 17 18console.log(child.name) // child 19console.log(child.hobby) // [ 'football', 'basketball', 'soccer' ] 20console.log(child.getHobby()) // [ 'football', 'basketball', 'soccer' ]

寄生组合式继承

  • 将组合继承,寄生式继承组合起来实现的继承,是所有继承方式的最优解
  • 优点:解决了组合继承父类会被调用两次和属性在不同层级会重复的问题
1function Parent() { 2 this.name = 'parent' 3 this.hobby = ['football', 'basketball'] 4} 5 6Parent.prototype.getHobby = function () { 7 return this.hobby 8} 9 10function Child() { 11 Parent.call(this) 12 this.friend = 'child friends' 13} 14 15function clone(parent, child) { 16 child.prototype = Object.create(parent.prototype) 17 child.prototype.constructor = child 18} 19 20clone(Parent, Child) 21 22Child.prototype.getFriend = function () { 23 return this.friend 24} 25 26let child = new Child() 27console.log(child.getHobby()) // [ 'football', 'basketball' ] 28console.log(child.getFriend()) // child friend

class 继承

  • 通过 extendssuper 实现
1class Parent { 2 constructor(name) { 3 this.name = name 4 } 5 getName() { 6 console.log(this.name) 7 } 8} 9class Child extends Parent { 10 constructor(name) { 11 super(name) 12 this.age = 18 13 } 14}