寄生组合继承

246 阅读5分钟
(1)原型链继承实现原理:

说明:Parent 类是【父构造函数】 Son 类是【子构造函数】

原型链继承基本思想就是Son 类的原型对象属性【 Son.prototype 】指向 new Parent( )。即

    function Parent(name,age){
        this.name=name
        this.age=age
    }
    function Son(favor,sex){
        this.favor=favor // 兴趣爱好
        this.sex=sex
    }
    Son.prototype=new  Parent("好好的",23) // 98
    let sonObj=new Son("篮球","男")
    

原型链继承实现的本质是改变Son构造函数的原型对象变量的指向【 就是Son.prototype的指向 】,Son.prototype= new Parent ( )。那么 Son.prototype 可以访问 Parent 对象空间的属性和方法。所以顺着 [proto ]属性 ,Son类也可以访问 Parent 类 的原型对象空间中的所有属性和方法。

原型链继承查找属性和方法的完整路线描述: 子对象首先在自己的对象空间中查找要访问的属性或方法,如果找到,就输出,如果没有找到,就沿着子对象中的proto属性指向的原型对象空间中去查找有没有这个属性或方法,如果找到,就输出,如果没有找到,继续沿着原型对象空间中的proto查找上一级原型对象空间中的属性或方法,直到找到Object.prototype原型对象属性指向的原型对象空间为止,如果再找不到,就输出null

(2)原型链继承实现容易被遗忘的重要一步

Son.prototype.constructor = Son

(3) 原型链继承常见疑问

Son.prototype= Parent.prototype 这样作为原型链继承的模式和 Son.prototype=new Parent (...) 又有什么区别呢?

(4)原型链继承的不足

局限性:不能通过子类构造函数向父类构造函数传递参数

慕课网 TS 高级课程

深度掌握 TS 继承准备:借用构造函数(冒充对象继承)

(1)借用构造函数继承如何解决原型链继承的局限性

借用构造函数继承思想就是在子类【 ChinesePeople 构造函数】的内部借助 apply ( ) 和 call ( ) 方法调用并传递参数给父类【 People 构造函数】,在父类构造函数中为当前的子类对象变量【ChinesePeopl对象变量】增加属性【本例中增加了name,

    function Parent (name, age) {
      this.name = name
      this.age = age
      console.log("this:", this)
      console.log("this.name:", this.name)
    }
    Parent.prototype.friends = ["xiaozhang", "xiaoli"]
    Parent.prototype.eat = function () {
      console.log(this.name + " 吃饭");
    }
    function Son (name, age, favor, sex) {
      this.favor = favor // 兴趣爱好
      this.sex = sex
      Parent.call(this, name, age)// TS继承中使用super
    }
    let sonobj2 = new Son("lisi", 34, "打篮球", "男");
    console.log("sonobj2:", sonobj2)
    console.log("sonobj2.friends:", sonobj2.friends);//undefined

(2)借用构造函数继承的不足

借用构造函数实现了子类构造函数向父类构造函数传递参数,但没有继承父类原型的属性和方法,无法访问父类原型上的属性和方法。

深度掌握 TS 继承准备:借用构造函数+原型链继承组合模式

(1)借用构造函数+原型链继承组合模式的优势

优势1: 具备借用构造函数的优点:子类【 ChinesePeople 构造函数】的内部可以向父类【 People 构造函数】 传递参数

优势2: 具备原型链继承的优点:ChinesePeople.prototype 和 new ChinesePeople( ) 出来的实例对象变量和实例都可以访问父类【 People 构造函数】 原型对象上的属性和方法。

    function People(name,sex,phone){// People父构造函数【看成是一个父类】//=Parent
            this.name=name; // 实例属性
            this.sex=sex;
            this.phone=phone
    }       
    People.prototype.doEat=function(){
        console.log(this.name + "吃饭...")
    }
    function ChinesePeople(name,sex,phone,national){ //=SON
        People.apply(this,[name,sex,phone]);// 借用父构造函数继承
        this.national=national;// 民族
    }
    ChinesePeople.prototype=new People("wangwu",'男',"1111"); 
(2)借用构造函数+原型链继承组合模式的不足:

缺点:调用了两次父类构造函数 【 People 构造函数】 new People 调用构造函数带来问题:

  1. 进入 People 构造函数为属性赋值,分配内存空间,浪费内存;
  2. 赋值导致效率下降一些,关键是new People 赋的值无意义,出现代码冗余,new ChinesePeople出来的对象和这些值毫不相干,是通过子类 ChinesePeople 构造函数中的 apply 来向父类People构造函数赋值。

深度透彻掌握寄生组合继承【实现方法1+优化 】【最佳继承模式】

寄生组合继承模式=借用构造函数继承+寄生继承。

寄生组合继承既沿袭了借用构造函数+原型链继承两个优势,而且解决了借用构造函数+原型链继承调用了两次父类构造函数为属性赋值的不足。寄生组合继承模式保留了借用构造函数继承,寄生组合继承模式使用寄生继承代替了原型链继承。

什么是寄生继承呢?就是 ChinesePeople.prototype 不再指向 new People( ) 出来的对象空间,而用 People 类 【父构造函数】的原型对象属性“克隆”了一个对象。再让ChinesePeople.prototype指向这个新对象,很好的避免了借用构造函数+原型链继承调用了两次父类构造函数为属性赋值的不足。

具体执行步骤见下面代码。

解释 S99 行代码:表示创建了一个新对象,相当用 People 类 【父构造函数】的原型对象属性“克隆”了一个对象。

解释 S100 行代码:让ChinesePeople 原型对象变量指向S99

    "克隆"对象实现方式代码优化 [ TS 继承和装饰器底层继承实现模式]
    function createProtoTypeClone(prototypeObject){
      function Middle(){
          // 第四步在新创建的ChinesePeople原型对象空间的Middle实例对象中添加constructor属性
          // 使其指向子类ChinesePeople构造函数对象空间
           this.constructor=ChinesePeople;
          // clone.constructor=ChinesePeople;
      }
        
      // 第一步.创建Person父类原型的副本
      Middle.prototype=Person.prototype;
      // 第二步2.1:创建父类原型副本的实例对象
      return new Middle();      
    }
​
    // 第二步2.2 clone指向父类原型副本实例对象的变量
    vSonr clone=createProtoTypeClone(Person.prototype);
​
    // 第三步:设置子类原型为该副本的实例对象
    ChinesePeople.prototype=clone;
​
    
    "克隆"对象实现方式2
    // 第S99行效果 = S102+S103。相当用 People 类【父构造函数】的原型对象属性“克隆”了一个对象。
    let cloneOneParentObj=Object.create(People.prototype)  // S99
    ChinesePeople.prototype=cloneOneParentObj // S100
    ChinesePeople.prototype.constructor = ChinesePeople;// S101