JavaScript中的几种常见继承,你都知道吗?

217 阅读4分钟

什么是继承?

继承就是利用原型让一个引用类型继承另一个引用类型的属性和方法。

常见的几种继承

  • 原型链继承
  • 构造函数继承
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承
  • class继承

1. 原型链继承

函数都有一个prototype,该属性指向一个对象(这个对象指的就是函数的原型对象) var instance = new SubType() //instance 就是原型对象

// 原型链继承

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


function SubType() {
  this.subproperty = false
}

//修改原型链指向
SubType.prototype = new SuperType() // { property: true, __proto__: SuperType.prototype }

var instance = new SubType()  

instance.getSuperValue() // true

 // instance.__proto__ == SubType.prototype = new SuperType()  
 // instance: {       
                subproperty: false, 
                __proto__: { 
                  property: true, 
                  __proto__: SuperType.prototype 
                }
               }

该方法的缺点:

  • 原型实际上会变成另一个类型的实例,并且这个实例中的属性会被所有的子类实例所共享(也就是说以后 new SubType()的对象的隐式原型都指向了 new SuperType() 而不再指向 SubType)
  • 在创建子类型的实例时,做不到向超类型的构造函数传参 (按正常情况下 var instance = new SubType('xxx'), instance可以向SubType传参,但是instance被修改原型链指向后,不能向SuperType上传参。 )

2.构造函数继承

构造函数继承也叫经典继承原理简单来说就是将SuperType的this指向SubType。让SubType的构造对象可以访问到SuperType里面的属性。

// 经典继承

SuperType.prototype.getSuperValue = function () {
  return this.property
}
function SuperType() {
  this.colors = ['red','green','blue']
}


function SubType(name) {
  SuperType.call(this,name)
  
}
var instance1 = new SubType('')
console.log(instance1.colors);  //['red','green','blue']
//经典继承可以解决原型链继承存在的问题
instance1.colors.push('pink')
console.log(instance1.colors);  //[ 'red', 'green', 'blue', 'pink' ]

var instance2 = new SubType()
console.log(instance2.colors); //['red','green','blue']
// 这时SubType这个实例中的属性就不会被所有的子类实例所共享了。


//而且也可以向父类传参了
var instance1 = new SubType('西瓜')

不过这个经典继承也有缺点:

  • "子类实例" 无法继承到超类构造函数 "原型上" 的属性和方法。(在SuperType上挂载的属性和方法子类继承不到)
  • 所有的属性和方法都只能写在构造函数内部,降低了代码的复用性。

3. 组合继承

组合继承也叫伪经典继承。听名字也知道,是组合了原型链和构造函数继承方法的一种继承。 组合继承很好了解决了原型链继承和经典继承存在的问题。

//组合继承
SuperType.prototype.sayName= function(){
  return this.name
}
function SuperType(name){
  this.name = name
  this.colors = ['red','green','blue']
}

function SubType(name,age){
  this.age = age
  SuperType.call(this,name)  //经典继承
}

SubType.prototype = new SuperType()    //原型链继承
SubType.prototype.constructor = SubType //将构造器改回来

//1.  SuperType.call(this,name) 继承到了父类的属性和方法,并且可以传参
var instance1 = new SubType('喜羊羊',20)  
console.log(instance1.colors); // [ 'red', 'green', 'blue' ]

//2. SubType实例中的属性不会被所有的子类实例所共享
instance1.colors.push('pink')
console.log(instance1.colors);  //[ 'red', 'green', 'blue', 'pink' ]
var instance2 = new SubType('灰太狼',18)
console.log(instance2.colors);  // [ 'red', 'green', 'blue' ]

// 3.可以继承到原型链上的属性和方法
var instance1 = new SubType('懒洋洋',20)
console.log(instance1.sayName()); // 懒洋洋

//4. SubType.prototype.constructor = SubType 将父类原型改回
  这样也可以在SubType的原型上挂载属性和方法。

但是就算如此,组合继承也存在着一定的缺点:

那就是 超类构造函数无论何时,都要被调用俩次。

第一次:SubType.prototype = new SuperType()

第二次:SuperType.call(this,name)

4.原型式继承

基于已有的对象创建新对象,但是还不会因此创建新定义的一种方法。

先了解一个方法。Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

var person = {
  name : '喜羊羊',
  colors: ['red','green','blue']
}

var person1 = Object.create(person,{
  name : {
    value : '灰太狼'
  }
}) //让person1继承到peroson

var person2 = Object.create(person,{
  name : {
    value : '懒洋洋'
  }
})

person1.colors.push('pink')
console.log(person1.name);   // 灰太狼
console.log(person2.name);   //懒洋洋
console.log(person1.colors); //[ 'red', 'green', 'blue', 'pink' ]
console.log(person2.colors); //[ 'red', 'green', 'blue', 'pink' ]

缺点也比较明显:
- 包含引用类型值的属性始终会共享

5.寄生式继承

寄生式继承和原型式继承有写相似,寄生式继承只考虑对象,而不考虑自定义的类型和构造函数的情况。

function creatPerson(original) {
  var clone = Object.create(original)
  clone.sayGood = function () {
    console.log('hello');
  }
  return clone
}

let obj= {
  age:20
}
let obj2 = creatPerson(obj)
console.log(obj2.age); //20
obj2.sayGood()         //hello

缺点:

  • 做不到函数的复用

6.寄生组合式继承

一听这名字就很屌,直接看代码

// 寄生组合式继承

SuperType.prototype.sayName= function(){
  return this.name
}
function SuperType(name){
  this.name = name
  this.colors = ['red','green','blue']
}
function SubType(name,age){
  this.age = age
  SuperType.call(this,name)  //经典继承
}

//将组合式的原型链继承改成原型式继承,减少超类的两次调用
var anotherPrototype = Object.create(SuperType.prototype)  //原型式继承
SubType.prototype = anotherPrototype  
SubType.prototype.constructor = SubType 

var instance1 = new SubType('南阳',20)
instance1.colors.push('pink')       //[ 'red', 'green', 'blue', 'pink' ]
console.log(instance1.colors);
var instance2 = new SubType('酱总',18)
console.log(instance2.colors);      //[ 'red', 'green', 'blue' ]

这个寄生组合式继承是这七种里面最完美的方案了。

7.class继承

extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。constructor表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError错误,如果没有显式指定构造方法,则会添加默认的 constructor方法,使用例子如下。

// class继承

class Parent {
  constructor(value){
    this.val =value
  }

  getValue(){
    console.log(this.val);
  }
  
}

class Child  extends Parent {
  constructor(value ){
    super(value)
    this.val = value 
  }
}

let child = new Child(111)
child.getValue()  // 111


如果有问题,欢迎指正。