JavaScript中的繼承介紹

一、繼承的概念

繼承是所有面向?qū)ο笳Z言中最重要的一個(gè)特征。而執(zhí)行環(huán)境也是JS最為重要的一個(gè)概念。執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)的范圍,決定了它們各自的行為。

主要繼承方式:接口繼承和實(shí)現(xiàn)繼承

JS主要支持實(shí)現(xiàn)繼承,而且是在原型鏈的基礎(chǔ)上實(shí)現(xiàn)的,繼承是發(fā)生在對象與對象之間

二、原型鏈的概念

原型鏈作為實(shí)現(xiàn)繼承的主要方法,是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法

構(gòu)造函數(shù)、原型(對象)和構(gòu)造函數(shù)創(chuàng)造的對象之間的關(guān)系:每個(gè)構(gòu)造函數(shù)都有一個(gè)屬性 prototype 指向一個(gè)原型對象,每個(gè)原型對象也有一個(gè)屬性constructor指向函數(shù),通過new 構(gòu)造函數(shù)()創(chuàng)建出來的對象內(nèi)部有一個(gè)不可見的屬性[[proto]]指向構(gòu)造函數(shù)的原型。

1、更換構(gòu)造函數(shù)的原型

原型其實(shí)就是一個(gè)對象,只是默認(rèn)情況下原型對象是瀏覽器會(huì)自動(dòng)幫我們創(chuàng)建的,而且自動(dòng)讓構(gòu)造函數(shù)的prototype屬性指向這個(gè)自動(dòng)創(chuàng)建的原型對象。我們完全可以將原型對象更換成我們自定義類型的對象。

    //定義一個(gè)構(gòu)造函數(shù)。
    function Father () {
        // 添加name屬性.  默認(rèn)直接賦值了。當(dāng)然也可以通過構(gòu)造函數(shù)傳遞過來
        this.name = "馬云";
    }
    //給Father的原型添加giveMoney方法
    Father.prototype.giveMoney = function () {
        alert("我是Father原型中定義的方法");
    }
    //再定義一個(gè)構(gòu)造函數(shù)。
    function Son () {
        //添加age屬性
        this.age = 18;
    }
    //關(guān)鍵地方:把Son構(gòu)造方法的原型替換成Father的對象。  因?yàn)樵褪菍ο?,任何對象都可以作為原?    Son.prototype = new Father();
    //給Son的原型添加getMoney方法
    Son.prototype.getMoney = function () {
        alert("我是Son的原型中定義的方法");
    }
    //創(chuàng)建Son類型的對象
    var son1 = new Son();

以上代碼即實(shí)現(xiàn)了Son繼承了Father的過程

注意:繼承過程中的訪問屬性和方法的過程為:

對象本身->原型->原型的原型->...->原型鏈的頂端(Object原型對象)這也是為什么我們隨意創(chuàng)建一個(gè)對象,就有很多方法可以調(diào)用,其實(shí)這些方法都是來自O(shè)bject的原型對象(Object原型對象的原型有人說是null,也有人說是它本身)

2、測試數(shù)據(jù)類型的四種方法

2.1、typeof:一般用來測試簡單數(shù)據(jù)類型和函數(shù)的類型。如果用來測試對象,則會(huì)一直返回object,沒有太大意義。

2.2、instanceof:用來測試一個(gè)對象是不是屬于某個(gè)類型。結(jié)果為boolean值。

function Father () {
    }
    function Son () {   
    }

    Son.prototype = new Father();
    var son = new Son();
    alert(son instanceof Son);  // true
    // Son通過原型繼承了Father
    alert(son instanceof Father);  // true
    //Father又默認(rèn)繼承了Objcet
    alert(son instanceof Object); // true

2.3、isPrototypeOf( 對象 ) : 這是個(gè) 原型對象 的方法,參數(shù)傳入一個(gè)對象,判斷參數(shù)對象是不是由這個(gè)原型派生出來的。 即判斷這個(gè)原型是不是參數(shù)對象原型鏈中的一環(huán)。

    function Father () {
        
    }
    function Son () {
        
    }

    Son.prototype = new Father();
    var son = new Son();
    alert(Son.prototype.isPrototypeOf(son));  // true
    alert(Father.prototype.isPrototypeOf(son)); // true
    alert(Son.prototype.isPrototypeOf(Father)); //false
    alert(Object.prototype.isPrototypeOf(Father)) //true
    alert(Object.prototype.isPrototypeOf(son)); // true
    
    //從參數(shù)對象原型開始

2.4、Object.prototype.toString.call(對象或基本類型)

    function Foo(){
    
    }
    
    var arr = new Array("a");
    var s = arr.toString();
    console.log(s);
    console.log(Object.prototype.toString.call(foo))  //測試數(shù)據(jù)類型

三、 原型鏈在繼承中的缺陷

1、在原型鏈中,父類型的構(gòu)造函數(shù)創(chuàng)建的對象,會(huì)成為子類型的原型。則父類型中定義的實(shí)例屬性,就會(huì)成為子類型的原型屬性。對子類型來說,這和我們以前說的在原型中定義方法,構(gòu)造函數(shù)中定義屬性是違背的。子類型原型(父類型對象)中的屬性被所有的子類型的實(shí)例所共有,如果有一個(gè)實(shí)例(即構(gòu)造函數(shù)創(chuàng)建的對象)去更改,則會(huì)很快反應(yīng)的其他的實(shí)例上。

    function Father () {
        this.girls = ["志玲", "鳳姐"];
    }
    function Son () {
        
    }
    // 子類的原型對象中就有一個(gè)屬性 girls ,是個(gè)數(shù)組
    Son.prototype = new Father();   
    var son1 = new Son();
    var son2 = new Son();
    //給son1的girls屬性的數(shù)組添加一個(gè)元素
    son1.girls.push("亦非");
    //這時(shí),發(fā)現(xiàn)son2中的girls屬性的數(shù)組內(nèi)容也發(fā)生了改變
    alert(son2.girls);  // "志玲", "鳳姐", "亦非"

2、向父類型的構(gòu)造函數(shù)中傳遞參數(shù)問題
如以上,父對象的屬性和方法會(huì)被子對象(實(shí)例所共享)則父類型傳遞的參數(shù)也會(huì)被子對象(實(shí)例所共享):父類型傳遞的地方:Son.prototype = new Father();

四、借用構(gòu)造函數(shù)調(diào)用"繼承"(函數(shù)借調(diào))

為解決以上屬性共享問題:
借用構(gòu)造函數(shù)調(diào)用繼承,又叫偽裝調(diào)用繼承或冒充調(diào)用繼承。雖然有了繼承兩個(gè)字,但是這種方法從本質(zhì)上并沒實(shí)現(xiàn)繼承,只是完成了構(gòu)造方法的調(diào)用而已。

1、借調(diào)方法

主要有call和apply方法:功能都是更改一個(gè)構(gòu)造方法內(nèi)部的this指向到指定的對象上。(后面詳細(xì)有介紹)

            function Father(x , y ){
                this.x = x;
                this.y = y;
                
            }
            function Son(x , y , z){
                Father.apply(this , [x , y]);//將數(shù)組展開,但是call不會(huì)
                this.z = z;
            }
            var s1 = new Son(1 , 2 , 3);
            console.log(s1);//Son {x: 1, y: 2, z: 3}

call和apply應(yīng)用

    var name = "a"
    function foo(a, b){
        console.log(a + "  " + b);
        this.name = "d"
    }
    var obj = {
        name : "c"
    }
    foo.call(obj, 1, 3);  //不影響后面內(nèi)容
    console.log(obj.name);  //"d"
    
//計(jì)算一個(gè)數(shù)組中的最大值和最小值
    var arr = [30, 6, 9, 30, 50];
    var max = Math.max.apply(Math, arr)//50
    console.log(max);
    console.log(Math.min.apply(Math, arr));//6

注意:函數(shù)借調(diào)其實(shí)并沒有真的繼承,僅僅是調(diào)用了Father構(gòu)造函數(shù)而已。也就是說,son對象和Father沒有任何的關(guān)系。

通過函數(shù)借調(diào)方式將父類型傳遞的屬性值進(jìn)行覆蓋,從而實(shí)現(xiàn)了子對象(實(shí)例)屬性不相同的效果。
最后編輯于
?著作權(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)容