继承方式有哪些,优缺点都在这了

983 阅读4分钟

继承?何为继承?

通俗易懂的理解: 你爸爸名下的财产以后都是你的。也就是说子类可以拥有父类的所有东西。并且子类可以在自己身上扩展方法。

那么?继承都有哪些呢?

1.原型链继承

2.经典继承(构造函数的继承)

3.组合继承(顾名思义:原型链继承和经典继承的结合)

4.寄生式继承

5.组合寄生式继承

首先我们谈谈原型链继承

子类的原型成为父类的实例

//创建一个父类构造函数
function Super(value) {
    this.SuperValue = value
}
//给父类添加一个方法
Super.prototype.getSupervalue = function() {
        return this.SuperValue
    }
    //创建一个子类构造函数
function Sub(value) {
    this.Subvalue = value
}
//子类的原型去继承父类
Sub.prototype = new Super()
    //给子类添加一个方法
Sub.prototype.getSubvalue = function() {
    return this.Subvalue
}
const sub = new Sub(123)
console.log(sub.getSupervalue()); //undefined
console.log(sub.getSubvalue()); //123

这里要注意的是:子类添加方法一定要在子类继承父类以后,如果顺序反了,那么子类添加的所有原型方法都没用(因为子类的原型已经变成了父类的原型) 优点:

1.父类方法可以复用

缺点:

1.1. 父类的所有引用属性会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响

下图代码执行结果

function Parent() {
   this.isShow = true
   this.info = {
       name: "yhd",
       age: 18,
   };
}

Parent.prototype.getInfo = function() {
   console.log(this.info);
   console.log(this.isShow); // true
}

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

let Child1 = new Child();
Child1.info.gender = "男";
Child1.getInfo();  // {name: "yhd", age: 18, gender: "男"}

let child2 = new Child();
child2.getInfo();  // {name: "yhd", age: 18, gender: "男"}
child2.isShow = false

console.log(child2.isShow); // false

2.子类在创建实例的时候无法向父类构造函数传递参数

经典继承

在子类的构造函数中调用父类的构造函数,使用applycall函数,更改父类构造函数的指向,让子类自己生成父类构造函数中定义的属性和方法

//创建父类构造函数
function Super() {
    this.SuperArray = [1, 2, 3, 4]
    this.k=function(){console.log('我是父类的方法')}
}
//创建子类构造函数并使用call或者apply改变this的指向
function Sub() {
    Super.call(this)
}
const app = new Sub()
app.SuperArray.push(5)
console.log(app.SuperArray); //[1,2,3,4,5]

优点:

  1. 可以在子类构造函数中向父类传参数
  2. 父类的引用属性不会被共享
function Parent(name) {
    this.info = { name: name };
}
function Child(name) {
    //继承自Parent,并传参
    Parent.call(this, name);
    
     //实例属性
    this.age = 18
}

let child1 = new Child("yhd");
console.log(child1.info.name); // "yhd"
console.log(child1.age); // 18

let child2 = new Child("wxb");
console.log(child2.info.name); // "wxb"
console.log(child2.age); // 18

缺点:

1.类的方法要在构造函数中声明,函数得不到复用

//创建父类构造函数
function Super() {
    this.SuperArray = [1, 2, 3, 4]
    this.k=function(){console.log('我是父类的方法')}
}
//创建子类构造函数并使用call或者apply改变this的指向
function Sub() {
    Super.call(this)
}
const app = new Sub()
const app1=new Sub()
console.log(app.k==app1.k)//两个方法不是同一个方法(函数得不到服用) false
console.log(app1.k)
app.SuperArray.push(5)
console.log(app.SuperArray); //[1,2,3,4,5]

2.无法访问父类原型上的方法

//创建父类构造函数
function Super() {
    this.SuperArray = [1, 2, 3, 4]
}
//给父类添加原型方法
Super.prototype.getSuper=function(){
console.log('我是父类原型上的方法')
}
//创建子类构造函数并使用call或者apply改变this的指向
function Sub() {
    Super.call(this)
}
const app = new Sub()
app.getSuper() //这里会报错,这是父类原型上的方法,子类没有,所以报错
app.SuperArray.push(5)
console.log(app.SuperArray); //[1,2,3,4,5]

组合继承的实现

//父类构造函数
function Super(age) {
    this.Superage = [12, 15, 14, 18]
    this.age=age
}
//给父类构造函数的原型上添加方法
Super.prototype.sayage = function() {
    return this.age
}

//子类构造函数,并将父类拿到子类里面去
function Sub(age,name) {
    Super.call(this,age)
    this.name=name
}
//子类的原型作为父类的实例
Sub.prototype = new Super()
Sub.prototype.sayname=function(){
return this.name
}
//子类构造器指向自己
Sub.prototype.construct = Sub

const app = new Sub(19,'zjx')
app.Superage.push(12)
console.log(app.Superage) //[12,15,14,18,12]
console.log(app.sayage()) //19 
console.log(app.sayname()) //zjx

const app1 = new Sub(20,'kkk') //实现了向父类传值
console.log(app1.Superage) //[12,15,14,18]这里对比上面,实现了引用类型的改变不会影响其他的实列对象
console.log(app1.sayage()) //20
console.log(app1.sayname()) //kkk

优点:原型链继承和经典继承的优点他都有了 缺点:父类构造函数会被调用两次

寄生式继承的实现

核心:在原型式继承的基础上,增强对象,返回构造函数

function createAnother(original){
  var clone=Object.create(original); // 通过调用 object() 函数创建一个新对象
  clone.sayHi = function(){  // 以某种方式来增强对象
    alert("hi");
  };
  return clone; // 返回这个对象
}

缺点:

1.无法实现函数复用

var person = {name:'zjx',arr:[1,3,2,5]}; 
var person1 = {name:'kkk',arr:[1,2,5]}; 
var anotherPerson = createAnother(person); 
var anotherPerson1 = createAnother(person1); 
anotherPerson.sayHi(); //"hi"
anotherPerson1.sayHi(); //"hi"
console.log(anotherPerson.sayHi==anotherPerson1.sayHi)//false(说明得函数不到复用)

寄生式组合继承实现

结合了以上所有继承的优点

//父类
function Super(age){
    this.age=age
    this.color=['green','red']
    }
    //添加父类原型上的方法
    Super.prototype.getage=function (){
    return this.age
    }
    //子类
    function Sub(age,name){
    Super.call(this,age)
    this.name=name
    }
    //子类的原型作为父类的实例
    Sub.prototype=Object.create(Super.prototype)
    //子类的原型方法
    Sub.prototype.getname=function(){
    return this.name
    }
    // 修正子类原型的构造函数
    Sub.prototype.constructor = Sub
    let app=new Sub(12,'zjx') //子类的实例向父类传参
    app.color.push('pink')
    console.log(app.color) //green,red,pink
    console.log(app.getage())//12  //说明可以调父类函数上的方法
    console.log(app.getname())//zjx  
    
    let app1=new Sub(15,'kkk') 
    console.log(app1.color) //green,red 说明解决了子类实例改变父类属性的value值不会影响其他的实例对象