首頁  >  文章  >  web前端  >  js物件導向之繼承知識詳解

js物件導向之繼承知識詳解

小云云
小云云原創
2018-02-05 09:35:231442瀏覽

說到這個繼承,了解object-oriented的朋友都知道,大多數oo語言都有兩種,一種是介面繼承(只繼承方法簽章);一種是實作繼承(繼承實際的方法)本文主要和大家介紹js物件導向之繼承的相關知識,以及分享了讀者弄清楚這個知識點的學習心得,對此有需要的朋友參考下吧。希望能幫助大家。

奈何js中沒有簽名,因而只有實作繼承,而且靠的是原型鏈實現的。下面正式的說一說js中繼承那點事兒

1、原型鏈

原型鏈:實作繼承的主要方法,利用原型讓一個引用型別繼承另一個引用型別的屬性和方法。

回顧:建構函數,原型,實例三者的關係

每一個建構子都有一個原型物件(Person.prototype);原型物件都包含指向建構子的指標(constructor );每個實例都包含指向原型物件的指標(看不見的_proto_指標)

原型鍊是怎麼來的呢?

某個建構子的原型物件是另一個建構子的實例;這個建構子的原型物件就會有個(看不見的_proto_指標)指向另一個建構子的原型物件;

那麼另一個原型物件又是其他的建構子實例又會怎麼樣,就這樣層層遞進,形成原型鏈;來具體看一下吧


    //第一个构造函数;有一个属性和一个原型方法
    function SuperType(){
        this.property=true;
    } 
    
    SuperType.prototype.getSuperValue=function(){
        return this.property
    }


    //第二个构造函数;目前有一个属性
    function SubType(){
        this.subproperty=false
    }
    
    //继承了SuperType;SubType原型成了SuperType的实例;实际就是重写SubType的原型对象;给SuperType原型对象继承了
    SubType.prototype=new SuperType()
    
    //现在这个构造函数有两个属性(一个本身的subproperty,一个继承的存在原型对象的property);两个方法(一个原型对象的getSubValue,一个原型对象的原型对象的getSuperValue)
    SubType.prototype.getSubValue=function(){
        return this.subproperty
    }
    
    var instance=new SubType()  //创建第二个构造函数的实例

    console.log(instance.getSuperValue())  //true 先查找instance这个实例有没有此方法;显然没有,再查找SubType原型对象有没有此方法;也没有,再查找SubType原型对象的原型对象;显然是存在的

注意:instance的constructor現在指向的是SuperType這個建構子;因為原來的SubType.prototype被重寫了,其內部的constructor也就隨著SubType.prototype的原型物件的constructor指向建構函式SuperType;至於原型搜尋機制是怎麼樣運作的,請仔細看上面的程式碼,相信你是可以的

1.1完整的原型

在原型那節已經提了些,還是再說一下。完整的原型包括Object。

所有函數的預設原型都是Object的實例;每個預設原型都有個_proto_指標指向Object.prototype;因此自訂類型都繼承如toString,valueOf的方法

而Object.prototype的_proto_指標指向null來結束原型鏈。以Person建構函式為例,看看完整的原型鏈圖

1.2原型與實例的關係判斷

第一種使用instanceof運算子:測試實例和原型鏈中出現的建構函數,結果為true

第二種使用isPrototypeOf()方法: 只要是原型鏈中出現過的原型,都可以說是該原型鏈所衍生的實例的原型


    console.log(instance instanceof Object)   //都为true
    console.log(instance instanceof SuperType)
    console.log(instance instanceof SubType)

   
    console.log(Object.prototype.isPrototypeOf(instance)) //都为true
    console.log(SuperType.prototype.isPrototypeOf(instance))
    console.log(SubType.prototype.isPrototypeOf(instance))

1.3謹慎定義方法

注意:給原型物件添加方法,一定放在替換原型的後面,因為放在替換原型之前是找不到了,原型會被重寫的;

注意:在透過原型鏈繼承時,不能使用物件字面量來創建原型方法,因為也會重寫原型鏈;


#
    function SuperType(){
        this.property=true;
    } 
    
    SuperType.prototype.getSuperValue=function(){
        return this.property
    }
    
    function SubType(){
        this.subproperty=false
    }
    
    //继承SuperType
    SubType.prototype=new SuperType()
    
    //使用字面量添加新方法,导致上一行无效   因为现在的原型替换了Object实例而非SuperType的实例,关系中断
    SubType.prototype={
       getSubValue:function(){
           return this.subproperty;
       },
       somOtherMethod:function(){
           return false
       }
    };

    var instance=new SubType()
    console.log(instance.getSuperValue())  //error

1.4原型鏈的問題

1、包含引用型別值的原型:當實例是另一個函數的原型時,引用型別值就會變成原型上的屬性,就會被另一個函數的實例所共用。


    function SuperType(){
       this.colors=["yellow","red","olive"]
    }

    function SubType(){
    }

    SubType.prototype=new SuperType()  //color实际上就是原型上的了

    var instance1=new SubType()
    instance1.colors.push("purple")
    var instance2=new SubType()

    console.log(instance1.colors==instance2.colors)  //true

2、建立子類型實例時,不能傳遞參數給超類型的建構子參數(沒有辦法在不影響所有物件實例的情況下,給超類型的建構子傳遞參數)

2、借助建構子

為了解決原型中包含引用型別值所帶來的問題,利用建構子來解決

在子型別構造函數的內部呼叫超類型建構函數(函數是特定環境中執行程式碼的對象,可以透過apply或call呼叫)


    function SuperType(){
        this.color=["yellow","red","olive"]
    }

    function SubType(){
        //继承了SuperType
        SuperType.call(this)
    }

    var instance1=new SubType()
    instance1.color.push("purple")
    var instance2=new SubType()

    console.log(instance1.color)  //["yellow","red","olive","purple"]
    console.log(instance2.color)  //["yellow","red","olive"]


    //传递参数
    function SuperType(name){
       this.name=name
    }
    function SubType(){
        SuperType.call(this,"double")
        this.age=12
    }

    var instance1=new SubType()
    console.log(instance1.name)  //double
    console.log(instance1.age)  //12

問題:僅僅借鏡建構函數,那麼避免不了建構函式的問題,方法都在建構函式定義了,函式無法重複使用

3、組合繼承(常用的還是組合,和原型與建構結合一樣)


#
    function SuperType(name){
        this.name=name;
        this.color=["yellow","red","olive"];
    }

    SuperType.prototype.sayName=function(){
        console.log(this.name);
    }
 
    function SubType(name,age){
        //继承属性,创建属性副本
        SuperType.call(this,name);
        this.age=age;
    }
    
    //继承属性和方法,只是原型中属性被后来的函数调用生成的属性副本遮盖
    SubType.prototype=new SuperType();

    alert(SubType.prototype.constructor)  //指向的是SuperType

    SubType.prototype.constructor=SubType; //将constructor回归到SubType构造函数身上
    SubType.prototype.sayAge=function(){
        console.log(this.age)
    }
    
    
    var instance1=new SubType("double",23)
    instance1.color.push("pink")
    console.log(instance1.color)     //["yellow","red","olive","pink"]
    instance1.sayName()         //double
    instance1.sayAge()          //23

    var instance2=new SubType("single",34)
    console.log(instance2.color)     //["yellow","red","olive"]
    instance2.sayName()         //single
    instance2.sayAge()          //34

還有其他的繼承,花點時間寫一下

1、原型式繼承

克羅克福德寫的;借助原型可以基於已有的對象建立新對象,同時不必建立自訂類型


    function object(o){      //本质上object()函数对其中对象的浅复制
        function F(){}      //创建一个新的构造函数
        F.prototype=o      //构造函数原型为传入的对象
      return new F()      //返回构造函数的实例
    }

    var person={
        name:"double",
        friends:["tom","jack","mike"]
    }

    var person1=object(person)   //事实上为原型共享
    person1.name="grey"
    person1.friends.push("single")
    
    console.log(person1.friends)  //["tom", "jack", "mike", "single"]

    var person2=object(person)
    person2.name="red"
    console.log(person2.friends)   //["tom", "jack", "mike", "single"]

ES5為了規範原型式的繼承,有個Object.create()來方便,IE9以上可以;只是想一個物件和另一個物件保持類似的情況,完全可以這種方法


    var person={
        name:"double",
        friends:["tom","jack","mike"]
    }

    var person1=Object.create(person)
    person1.name="single"
    person1.friends.push("singles")

    var person2=Object.create(person)

    console.log(person1.friends==person2.friends) //true

    //Object.create()接受两个参数,一个为作为新对象原型的对象,一个为新对象定义额外属性对象
    var person={
        name:"double",
        friends:["tom","jack","mike"]
    }

    var person1=Object.create(person,{
         name:{ 
            value:"single"  //每个属性都是通过自己描述符定义的
         }
    })

2、寄生式繼承

思路和原型式繼承一脈相承,建立一個用於封裝繼承過程的函數,內部透過方式增強對象,返回對象;主要考慮對象時使用


function object(o){
       function F(){}
       F.prototype=o
       return new F()
    }

    function createPerson(original){
       var clone=object(original)   //继承原型
       clone.sayName=function(){ 
           alert("name")
       }
       return clone
    }

    var person={
       name:"double",
       friends:["single","tom","jack"]
    }

    var person1=createPerson(person)
    person1.sayName()  //name   引用类型值还是共享的

3、寄生組合繼承

組合繼承是繼承中常用到的,但是會呼叫兩次超類型建構函數;寄生組合繼承就是為了解決這個問題的


  function object(o){
     function F(){}
     F.prototype=o
     return new F()
  }


  function inheritPrototype(subType,superType){
     var prototype=object(superType)    //创建对象  (superType实例)
     prototype.constructor=subType     //增强对象
     subType.prototype=prototype      //指定对象  (原型赋予实例)
  }
  

   function SuperType(name,sex){
      this.name=name
      this.sex=sex
      this.colors=["red"]
   }

   SuperType.prototype.sayName=function(){
     alert(this.name)
   }

   function SubType(name,sex,age){
      SuperType.call(this,name,sex)
      this.age=age
   }


   inheritPrototype(SubType,SuperType)    //目前subType.prototype什么都没有
   SubType.prototype.sayAge=function(){   //为subType.prototype添加个方法
      alert(this.age)
   }

   var person1=new SubType("double","man",34)
   console.log(person1.name)  //SuperType 这是个Bug
   console.log(person1.sex)   //man
   console.log(person1.colors) //["red"]
   person1.sayAge()       //34

到此,差不多結束啦,感謝你對腳本之家的支持,希望我們整理的內容能幫助你。

相關推薦:

詳解JavaScript基於物件導向之繼承_javascript技巧

#

以上是js物件導向之繼承知識詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn