一文搞懂 JavaScript 的對象基礎、原型與繼承鏈(MDN 學習筆記)

面向對象的基礎

1.聲明一個對象

var myperson={
    name : "Zhang",
    age : 25,
    sex : "male",
    introduction:function(){
        alert(name+age+sex);
    }
};
myperson.introduction();

2.使用構造函數構造

構造函數與普通函數的區別在於調用方式,構造函數使用new方法構造。

function creatrPerson(name,age,sex,interests){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.interests = interests;
    this.bio = function(){
        var res = "";
        if(this.sex == "male"){
            res+="He ";
        }else if(this.sex=="female"){
            res+="She ";
        }else{
            res+="It "
        }
        for(var i=0;i<interests.length;i++){
            res+="likes "+interests[i];
            if(i!=interests.length-1){
                res+=" and ";
            }
        }
        alert(res);
    }
}
var zhang = new creatrPerson("zhang",18,"male",["basekatball","swiming","singing"]);
zhang.bio();

在這裏插入圖片描述
3.使用Object 構造

首先創建了一個空對象,之後使用點或括號表示法向此對象添加屬性和方法

var person1 = new Object();
person1.name = 'Chris';
person1['age'] = 38;
person1.greeting = function() {
	alert('Hi! I\'m ' + this.name + '.');
}

對象原型

原型基礎

JavaScript 常被描述爲一種基於原型的語言 (prototype-based language)——每個對象擁有一個原型對象。

注意:與Java的面向對象設計不同,java中存在類的概念,創建對象後,會將類的屬性和方法複製到對象中。
而JS中,會通過_proto_屬性來建立構造方法與對象實例的連接。

原型鏈

每個 function方法中都具有 prototype 屬性,其對應的也就是我們通過 function 構造出實例的 proto 屬性。接下來舉個例子。

function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );

對應輸出如下,可以看到實例對象的_proto_對應構造函數的 protoype(因爲設置了foo這個屬性)。同時注意一點,JS會在需要某個對象的屬性時,會首先去尋找該對象是否存在該屬性,之後會去對象的_proto_屬性中尋找,依次類推,如果找不到則會判斷該屬性爲Undefined,這條鏈路被稱爲原型鏈

doSomeInstacing對象 的 proto 屬性對應的的就是 dosomething構造函數 的 prototype 屬性

{
    prop: "some value",
    __proto__: {
        foo: "bar",
        constructor: ƒ doSomething(),
        __proto__: {
            constructor: ƒ Object(),
            hasOwnProperty: ƒ hasOwnProperty(),
            isPrototypeOf: ƒ isPrototypeOf(),
            propertyIsEnumerable: ƒ propertyIsEnumerable(),
            toLocaleString: ƒ toLocaleString(),
            toString: ƒ toString(),
            valueOf: ƒ valueOf()
        }
    }
}

我們可以進一步理解原型鏈。我們使用之前cteatePerson方法去構造一個對象 Zhang。可以看到 Zhang 對象有多個屬性或方法供我們使用。
在這裏插入圖片描述

  • 瀏覽器首先檢查,Zhang 對象是否具有 valueOf() 方法。
  • 如果沒有,則瀏覽器檢查 Zhang 對象的原型對象(_proto_屬性)是否 valueof() 方法。
  • 如果沒有,則會進而調用 createPerson 構造函數的 prototype 對象,去 Object 構造函數中尋找是否具有 valueOf() 方法。這裏有這個方法,於是該方法被調用。

原型鏈的訪問順序:Zhang->createPerson->Object

修改原型

1.prototype 屬性

繼承的屬性和方法都在prototype中定義。這也就是爲什麼上文訪問 Zhang.valueOf 能返回具體的值,因爲Object.prototype.valueOf 可以供任何繼承自它的對象使用。

而任何不在 prototype 中的屬性或方法僅能供 Object 自己使用,繼承自它的對象無法使用。

2.constructor 屬性

每個實例對象都從原型對象那裏繼承了一個構造器,下述代碼都會返回 CreatePerson()構造器。

person1.constructor
person2.constructor

我們同樣可以使用構造器來構造對象。

person3 = new person1.constructor("feng",20,"male",["hha","aaa"]);

3.修改原型

我們嘗試修改(添加)構造器中的prototype屬性。

creatrPerson.prototype.farewell = function() {
  alert(this.name + ' has left the building. Bye for now!');
}

之後調用對象實例的 farewell 方法。瀏覽器會通過原型鏈找到構造器的prototype對象,並調用farewell方法

zhang.farewell()

在這裏插入圖片描述
值得注意的是,我們首先創建的的對象實例,之後才爲構造器添加屬性(方法),對象實例仍然能通過原型鏈找到該方法。

證明了下游對象(Zhang)在創建時不是複製了上游對象(構造函數的prototype)的方法和屬性,而是建立了聯繫。在訪問下游對象未定義的方法時,下游對象可以通過原型鏈訪問到上游對象中的對應方法。

繼承

可以通過上述文章瞭解,JS的繼承和Java不同,是通過原型鏈來完成的。因此我們接着嘗試創建繼承自另一個對象的的JS對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章