溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2022-08-09 16:06:29 來源:億速云 閱讀:95 作者:iii 欄目:開發(fā)技術(shù)

這篇“JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)”文章吧。

    前言

    繼承也是面向?qū)ο蟮奶匦灾?,但是?ES6 版本之前是沒有 extends 去實(shí)現(xiàn)繼承的,我們只能通過 構(gòu)造函數(shù) 和 原型對象 來實(shí)現(xiàn)繼承,其中分別為構(gòu)造函數(shù)來繼承屬性,原型對象來繼承方法,這種繼承模式被稱為 組合繼承

    一:call() 的作用與使用 

    在開始講解組合繼承前我們先來了解一下 call() 方法,call() 方法可以改變 this 的指向,也可以調(diào)用函數(shù)等等,最主要的還是其改變指向的作用

    語法格式call( 目標(biāo)this指向,參數(shù)1,參數(shù)2 ......)

    1.1 使用 call() 來調(diào)用函數(shù) 

    call() 可以拿來直接用來調(diào)用函數(shù)

         <script>
            function eat(){
                console.log('我在吃午飯');
            }
            eat.call()
         </script>

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    1.2 使用 call() 來改變 this 的指向 

    call() 的第一個(gè)參數(shù)為你要改變的 this 的指向,這里的 this 指的是 call 的調(diào)用者,此處函數(shù)調(diào)用不指定的話即指向 window,指定讓其指向新創(chuàng)建的對象 obj,只需要讓其第一個(gè)參數(shù)為 obj 對象即可,所以結(jié)果應(yīng)該是第一個(gè)為 window,第二個(gè)為 obj 對象

         <script>
            function eat(){
                console.log(this);
            }
            var obj={
                'name':'小明',
                'age':18
            }  
            eat.call()
            eat.call(obj)
         </script>

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

     二:利用構(gòu)造函數(shù)繼承父屬性

    我們已經(jīng)知道組合繼承是由構(gòu)造函數(shù)和原型對象一起來實(shí)現(xiàn)的,其中構(gòu)造函數(shù)實(shí)現(xiàn)的是屬性的繼承,原型對象實(shí)現(xiàn)的是方法的繼承,這版塊就走進(jìn)利用父構(gòu)造函數(shù)完成屬性的繼承

    2.1 實(shí)現(xiàn)過程 

    其實(shí)現(xiàn)非常容易,只需要在子構(gòu)造函數(shù)中,使用 call 調(diào)用父構(gòu)造函數(shù)(將其當(dāng)做普通函數(shù)調(diào)用),其中在 call 方法中更改父構(gòu)造函數(shù)中的 this 指向,由于 call 方法是在子構(gòu)造函數(shù)中調(diào)用的,所以此處當(dāng)做參數(shù)的 this 代表父構(gòu)造函數(shù)中的 this 指向子構(gòu)造函數(shù)的實(shí)例化對象,并傳參進(jìn)去,所以相當(dāng)于給子構(gòu)造函數(shù)的實(shí)例化對象添加了屬性并賦值

         <script>
            //聲明父構(gòu)造函數(shù)
            function Father(uname,uage,utel,sex){
                this.uname=uname;
                this.uage=uage;
                this.utel=utel;
                this.sex=sex;
            }
            //聲明子構(gòu)造函數(shù),但是想繼承父類的uname,uage,utel等等屬性的賦值操作
            function Son(uname,uage,utel,sex){
                Father.call(this,uname,uage,utel,sex)
            }
            var son1=new Son('張三',19,12345,'男')
            console.log(son1);
         </script>

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    2.1 實(shí)現(xiàn)過程分析

    • 首先在子構(gòu)造函數(shù)中使用 call 調(diào)用了父構(gòu)造函數(shù),并傳參給 call 的參數(shù),其中第一個(gè)參數(shù)為 this 指向的改變,其余為帶入的屬性值參數(shù)

    • 我們知道構(gòu)造函數(shù)中的 this 指向其實(shí)例化對象,所以本身父構(gòu)造函數(shù)的 this 應(yīng)該指向父構(gòu)造函數(shù)的實(shí)例化對象,而此處 call 方法調(diào)用在子構(gòu)造函數(shù)中,所以參數(shù)的指向更改為指向子構(gòu)造函數(shù)的實(shí)例化對象

    • 此處子構(gòu)造函數(shù)的實(shí)例化對象就是 son1,所以父構(gòu)造函數(shù)中的 this 指向的均是 son1,

    • 所以就給 son1 添加并賦值了 uname,uage 等等屬性

    三:利用原型對象繼承父方法 

    組合繼承的最后一版塊,利用原型對象來繼承方法,此處我們說明的是存放在構(gòu)造函數(shù)的原型對象里的公共方法的繼承

    3.1 繼承父方法的錯(cuò)誤演示

    錯(cuò)誤的繼承就是直接將父親的原型對象賦值給子的原型對象,這樣確實(shí)也可行,但是如果給子原型對象添加子類特有的方法,那父原型對象也會(huì)加上這個(gè)方法

         <script>
            //聲明父構(gòu)造函數(shù)
            function Father(uname,uage){
                this.uname=uname;
                this.uage=uage;
            }
            Father.prototype.money=function(){
                console.log('我有很多錢');
            }
            //聲明子構(gòu)造函數(shù)
            Son.prototype=Father.prototype;
            function Son(uname,uage){
                Father.call(this,uname,uage)
            }
            var father1=new Father('爸爸',40)
            var son1=new Son('兒子',19)
            console.log(father1);
            console.log(son1);
         </script>

     我們可以發(fā)現(xiàn)父子的原型對象中確實(shí)都有了這個(gè)方法,證明確實(shí)這個(gè)辦法是行得通的

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    但是其也有問題存在,當(dāng)我們想給子原型對象單獨(dú)添加其特有的方法時(shí),就會(huì)出問題

    上述問題給子原型對象添加特有方法的錯(cuò)誤示例:

         <script>
            //聲明父構(gòu)造函數(shù)
            function Father(uname,uage){
                this.uname=uname;
                this.uage=uage;
            }
            Father.prototype.money=function(){
                console.log('我有很多錢');
            }
            //聲明子構(gòu)造函數(shù)
            Son.prototype=Father.prototype;
            Son.prototype.school=function(){
                console.log('我去上學(xué)了');
            }
            function Son(uname,uage){
                Father.call(this,uname,uage)
            }
            var father1=new Father('爸爸',40)
            var son1=new Son('兒子',19)
            console.log(father1);
            console.log(son1);
         </script>

    我們發(fā)現(xiàn),我們確實(shí)給兒子添加上了兒子特有的方法,但是,父親的原型對象內(nèi)也加上了這個(gè)方法,這并不滿足我們的預(yù)期,原因分析如下

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    問題原因 

    問題就在于我們的原型對象也是對象,對象是引用數(shù)據(jù)類型,引用數(shù)據(jù)類型的對象本質(zhì)是在堆內(nèi)存存放,是不能直接訪問的,其訪問是通過棧內(nèi)存上的引用地址來找到去訪問,而我們此處采用的等號(hào)賦值的方式,實(shí)際上是將其在棧內(nèi)存上的引用地址拷貝過去了,二者指向了同一塊內(nèi)存空間,所以更改子原型對象,父原型對象也改變了

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    3.2 繼承父方法的正確做法

    正確的做法是讓其子原型對象對象等于父實(shí)例化對象  Son.prototype=new Father(),其實(shí)我感覺有種高內(nèi)聚低耦合的韻味,減少了直接聯(lián)系從而解決問題

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

         <script>
            //聲明父構(gòu)造函數(shù)
            function Father(uname,uage){
                this.uname=uname;
                this.uage=uage;
            }
            Father.prototype.money=function(){
                console.log('我有很多錢');
            }
            //聲明子構(gòu)造函數(shù)
            Son.prototype=new Father();
            Son.prototype.school=function(){
                console.log('我去上學(xué)了');
            }
            function Son(uname,uage){
                Father.call(this,uname,uage)
            }
            var father1=new Father('爸爸',40)
            var son1=new Son('兒子',19)
            console.log(father1);
            console.log(son1);
         </script>

     問題得以解決,子原型對象有了自己特有的方法,并且也繼承了父親原型對象中的方法

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    3.2 繼承父方法的注意事項(xiàng)

    我們以 Son.prototype=new Father() 這種方法繼承,看似已經(jīng)天衣無縫,其實(shí)我們早就說過,采用等號(hào)賦值的方法會(huì)造成原型對象被覆蓋,里面的構(gòu)造函數(shù) constructor 會(huì)被覆蓋掉,需要我們手動(dòng)返回,所以七千萬要記得手動(dòng)返回 constructor

         <script>
            //聲明父構(gòu)造函數(shù)
            function Father(uname,uage){
                this.uname=uname;
                this.uage=uage;
            }
            Father.prototype.money=function(){
                console.log('我有很多錢');
            }
            //聲明子構(gòu)造函數(shù)
            Son.prototype=new Father();
            Son.prototype.constructor=Son;  //手動(dòng)返回構(gòu)造函數(shù)constructor
            Son.prototype.school=function(){
                console.log('我去上學(xué)了');
            }
            function Son(uname,uage){
                Father.call(this,uname,uage)
            }
            var father1=new Father('爸爸',40)
            var son1=new Son('兒子',19)
            console.log(father1);
            console.log(son1);
            console.log(Son.prototype.constructor);
         </script>

    JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)

    補(bǔ)充:缺點(diǎn)

    就是 “對象原型繼承的this對象+prototype對象,都在對象的原型上 suber1._ proto _”,suber1._ proto _上的引用屬性任然是共享;

    所以就有了我們看到的一句勸告:盡量把屬性定義在構(gòu)造函數(shù)內(nèi),為了方便繼承吧;

    還有就是實(shí)例一個(gè)對象,執(zhí)行了兩次Super()

    以上就是關(guān)于“JavaScript之非extends的組合繼承怎么實(shí)現(xiàn)”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI