javascript的繼承

//8.繼承

構(gòu)造函數(shù)方式繼承

構(gòu)造函數(shù)方式繼承的缺點(diǎn):構(gòu)造函數(shù)的方法會(huì)在實(shí)例化的時(shí)候多次執(zhí)行,沒有必要。

    //8.1構(gòu)造函數(shù)方式繼承
    function SuperType1(){
        this.property = true
        this.arr = [1,2,3]
        this.fuc = function () {
            console.log('just test')
        }
    }
    function SubType1() {
        SuperType1.call(this)
        this.subproperty = false
    }
    var obj1 = new SubType1()
    var obj2 = new SubType1()
    obj1.arr.push(4)
    console.log(obj1.arr)//[1, 2, 3, 4]     引用類型沒有互相污染,但是fuc方法每次實(shí)例話都會(huì)新生成一次,沒必要。
    console.log(obj2.arr)//[1, 2, 3]     引用類型沒有互相污染,但是fuc方法每次實(shí)例話都會(huì)新生成一次,沒必要。

原型方式繼承

原型方式繼承缺點(diǎn):如果父級構(gòu)造函數(shù)內(nèi)有引用類型,如果此時(shí)有兩個(gè)實(shí)例對象,其中一個(gè)實(shí)例對象改變了引用類型的值,則另一個(gè)實(shí)例對象的引用類型值也會(huì)相應(yīng)改變。這不是我們期望的。

    //8.2原型方式繼承 (為什么原型繼承,因?yàn)闃?gòu)造函數(shù)方式繼承的話,每次new一個(gè)實(shí)例,相同的方法都會(huì)重新生成一次,沒有必要)
    function SuperType2(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType2.prototype.fuc = function(){//將構(gòu)造函數(shù)繼承里的fuc提出來到原型繼承里
        console.log('just test')
    }
    function SubType2(){
        this.subproperty = false
    }
    SubType2.prototype = new SuperType2()//將SubType2的prototype設(shè)置為需要繼承的構(gòu)造函數(shù)的實(shí)例
    var obj3 = new SubType2()
    var obj4 = new SubType2()
    obj3.arr.push(4)
    console.log(obj3.arr)//[1, 2, 3, 4]     原型鏈繼承方式,會(huì)讓引用類型的值發(fā)生改變互相污染
    console.log(obj4.arr)//[1, 2, 3, 4]     原型鏈繼承方式,會(huì)讓引用類型的值發(fā)生改變互相污染
    obj4.property = false
    console.log(obj3.property)//true    基本類型值不會(huì)污染
    console.log(obj4.property)//false   基本類型值不會(huì)污染

組合方式繼承(構(gòu)造函數(shù)+原型鏈)

由于構(gòu)造函數(shù)方式繼承會(huì)導(dǎo)致函數(shù)內(nèi)方法多次實(shí)例化(沒有必要),原型鏈繼承又有可能導(dǎo)致引用類型值的改變。所以現(xiàn)在考慮將兩者相結(jié)合。

    //8.3 構(gòu)造函數(shù)+原型鏈組合方式繼承(思路:把屬性放在構(gòu)造函數(shù)里,把方法提出到prototype里,子構(gòu)造函數(shù)的protoype指向父構(gòu)造函數(shù)的實(shí)例)
    function SuperType3(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType3.prototype.fuc = function(){//將構(gòu)造函數(shù)繼承里的fuc提出來到原型繼承里
        console.log('just test')
    }
    function SubType3(){
        //繼承屬性
        SuperType3.call(this)//等價(jià)于下方注釋部分,但由于是繼承,所以要這樣寫,因?yàn)榇蠖鄶?shù)時(shí)候你并不知道父構(gòu)造函數(shù)里有什么
        // this.property = true
        // this.arr = [1,2,3]
        this.subproperty = false
    }
    SubType3.prototype = new SuperType3()
    var obj5 = new SubType3()
    var obj6 = new SubType3()
    obj5.arr.push(4)
    console.log(obj5.arr)//[1, 2, 3, 4]     引用類型放在構(gòu)造函數(shù)內(nèi),沒有對其他實(shí)例造成污染
    console.log(obj6.arr)//[1, 2, 3]        引用類型放在構(gòu)造函數(shù)內(nèi),沒有對其他實(shí)例造成污染

組合方式優(yōu)化1

上述組合方式繼承還有問題,就是:
(1)SubType3.prototype = new SuperType3()時(shí)會(huì)調(diào)用SuperType3;
(2)SubType3內(nèi)部執(zhí)行SuperType3.call(this)時(shí)又會(huì)調(diào)用一次SuperType3。

//8.4 組合方式優(yōu)化1
    function SuperType4(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType4.prototype.fuc = function(){//將構(gòu)造函數(shù)繼承里的fuc提出來到原型繼承里
        console.log('just test')
    }
    function SubType4(){
        //繼承屬性
        SuperType4.call(this)//等價(jià)于下放注釋部分,但由于是繼承,所以要這樣寫,因?yàn)榇蠖鄶?shù)時(shí)候你并不知道父構(gòu)造函數(shù)里有什么
        // this.property = true
        // this.arr = [1,2,3]
        this.subproperty = false
    }
    SubType4.prototype =  SuperType4.prototype
    var obj7 = new SubType4()
    var obj8 = new SubType4()
    obj7.arr.push(4)
    console.log(obj7.constructor)//? SuperType4()

組合方式優(yōu)化2

上述優(yōu)化1依然有問題,雖然SuperType4只調(diào)用了一次,但是

...
SubType4.prototype =  SuperType4.prototype
...

這里又會(huì)導(dǎo)致SubType4實(shí)例化對象的constructor實(shí)際是指向SuperType4的。
所以,我們將此處修改為:

...
SubType4.prototype =  SuperType4.prototype
SubType4.prototype.constructor = SubType4
...
//加Object.create()前

這樣SubType4的原型對象的contructor屬性就指向SubType4了。但這樣就夠了嗎?我們發(fā)現(xiàn),此時(shí)SuperType4.prototype.constructor也指向了SubType4,這不是我們期望的。
所以還需要做以下這步:

...
SubType4.prototype =  Object.create(SuperType4.prototype)
SubType4.prototype.constructor = SubType4
...
//加Object.create()后

對比一下前后兩者區(qū)別:

//console.log(SuperType4.prototype)
{fuc: ?, constructor: ?}
   fuc:? ()
   constructor:? SuperType4()  //若直接SubType4.prototype =  SuperType4.prototype,SuperType4將會(huì)被替換成SubType4
   __proto__:Object

//console.log(Object.create(SuperType4.prototype))
SuperType4 {}
   __proto__:  //通過SubType4.prototype =  Object.create(SuperType4.prototype)創(chuàng)建一個(gè)新的對象,則會(huì)在__proto__平級處會(huì)增加一個(gè)constructor: SubType4。下方的SuperType4并不會(huì)被替換。
      fuc:? ()
      constructor:? SuperType4()
      __proto__:Object

上述代碼中,Object.create(SuperType4.prototype)基本等價(jià)于:

function object(o){//返回一個(gè)函數(shù)的實(shí)例,該實(shí)例的原型對象等于傳入的參數(shù)
  function F(){};
  F.prototype = o;
  return new F();
}
object(SuperType4.prototype)

也就是說,Object.create()方法會(huì)創(chuàng)建一個(gè)新的對象,對象的prototype即為傳入的參數(shù)。

最終,我們得到了最優(yōu)的繼承方式:

    function SuperType4(){
        this.property = true
        this.arr = [1,2,3]
    }
    SuperType4.prototype.fuc = function(){//將構(gòu)造函數(shù)繼承里的fuc提出來到原型繼承里
        console.log('just test')
    }
    function SubType4(){
        //繼承屬性
        SuperType4.call(this)//等價(jià)于下放注釋部分,但由于是繼承,所以要這樣寫,因?yàn)榇蠖鄶?shù)時(shí)候你并不知道父構(gòu)造函數(shù)里有什么
        // this.property = true
        // this.arr = [1,2,3]
        this.subproperty = false
    }
    SubType4.prototype =  Object.create(SuperType4.prototype)
    SubType4.prototype.constructor = SubType4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容