JavaScript中的原型、原型鏈、構造函數以及實例對象

其實原型我看了有一段時間了,也沒有看很長時間,就是找一個下午的時間聽聽課,然後就感覺很懵,似懂非懂的,接着就自己想想,然後就還是懵。平時翻翻技術類的公衆號或者CSDN上的文章也會涉及到原型的內容,加上前天我又看一一遍講解,看的多了慢慢的就更熟悉更瞭解了。
今天,下定決心要總結總結寫寫原型了。內容多少會有點問題,諒解諒解,畢竟是菜鳥的小白爲了記錄自己的學習過程。

構造函數和實例對象

構造函數也是一個函數,它與普通函數的區別就在於構造函數名首字母要大寫。通過構造函數創建對象的過程叫對象的實例化,創建的對象是實例對象。Object構造函數,是系統提供好的構造函數,我們自定義的構造函數也是構造函數。

var obj2 = new Object();
function Person(uname, uage) {
        this.uname = uname;
        this.uage = uage;
}
Person("xz", 18);

構造函數中的屬性和方法,分爲兩種:實例成員和靜態成員。一般構造函數中都是實例成員,靜態成員的添加方法是直接在構造函數上面添加的。
上面的uname和uage都是實例成員,靜態成員:Person.sex = “male”,這裏的sex就是靜態成員。

實例成員:構造函數內部通過this添加的屬性和方法
靜態成員:構造函數本身添加的屬性和方法

創建對象

再談談創建對象的幾種方式吧。
一般是三種方式:對象字面量創建、Object創建、自定義構造函數創建。其實總的來說是兩種方式:對象字面量創建和通過構造函數創建。

        // 1. 字面量創建對象
        var obj1 = {
            name: "lyf",
            age: 18
        }
        // 2. 通過 new Object
        var obj2 = new Object();
        obj2.name = "zly";
        obj2.age = 17;
        // 3. 通過自定義構造函數
        function Person(uname, uage) {
            this.uname = uname;
            this.uage = uage;
        }
        // 通過new創建出來的對象,是構造函數實例化出來的對象 —— 對象的實例化
        var obj3 = new Person("xz", 18);

new關鍵的過程

  1. 先創建一個空對象
  2. 把空對象賦值給this
  3. 把屬性和方法都掛在this上面
  4. 最後把this對象返回
    所以這就是在創建對象時,不需要返回值的原因。

原型

上面說完了構造函數和實例對象,接下來我們就來介紹介紹原型。
每個函數都有一個prototype屬性叫原型,這個屬性是一個對象,所以叫原型對象。
每個對象都有一個__proto__屬性,叫對象的原型,它指向構造該對象的構造函數的原型對象。

自定義構造函數:

// 只要是函數,就有原型對象
        function Person(uname, uage) {
            this.uname = uname;
            this.uage = uage;
        }
        // 構造函數也是函數, 所以構造函數也有自己的原型對象 
        var obj = new Person("xz", 18);
        // 通過prototype訪問函數的原型對象
        console.log(Person.prototype);

這就是原型對象。可以看到,原型對象裏有constructor和__proto__兩個屬性。
在這裏插入圖片描述
上面說了,只要是對象就有__proto__ 屬性,指向構造該對象的構造函數的原型對象。
__proto__是對象的原型,這裏__proto__指向創建Person.prototype對象的構造函數的原型對象 —— Object.prototype。
constructor是構造函數,指向創建該對象的函數。簡單來說,就是記錄誰創建了這個對象。這裏constructor指向創建Person.prototype對象的函數 —— Person。Person創建了Person.prototype原型對象,所以constructor指向Person。

		// 通過prototype訪問函數的原型對象
        console.log(Person.prototype); // Person的原型對象
        console.log(Person.prototype.__proto__); // Person原型對象的原型
        // 原型對象裏有一個constructor屬性,該屬性指向創建它(原型對象)的函數
        console.log(Person.prototype.constructor); 
        console.log(Person.prototype.__proto__ === Object.prototype);

第一個輸出的是Person構造函數的原型對象:Person.prototype。
第二個輸出的是Person構造函數的原型對象的原型:Object.prototype。Object是頂級對象,任何一個對象都是Object的實例化對象。
第三個輸出的是Person構造函數的原型對象的構造函數:Person。constructor屬性是指向創建該對象的構造函數。
第四個輸出的是true。
在這裏插入圖片描述

看了構造函數的原型,現在再來看下實例對象的原型

		var obj = new Person("xz", 18);
        // 構造函數實例化的對象
        console.log(obj);
        // 實例化對象有一個原型屬性:__ptoro__
        // 對象的__proto__ 屬性 指向構造函數的原型對象
        console.log(obj.__proto__);
        // 對象的原型 === 構造函數的原型對象
        console.log(obj.__proto__ === Person.prototype);

第一個輸出的是obj實例對象自己,對象除了有構造函數定義的屬性外,還有有__ptoto__屬性。
第二個輸出的是obj的原型 —— Person構造函數。因爲obj是通過Person構造函數實例化出來的,所以obj的原型是Person構造函數d的原型對象。
第三個輸出的是true。因爲obj的原型是Person構造函數原型對象,Person構造函數的原型對象是Person.prototype,所以輸出true。
在這裏插入圖片描述
來一張obj、Person、Person.prototype的圖,就明白了
在這裏插入圖片描述
這個圖再完善一下,加上Object就更清晰了。我們都知道,所有的對象到最後都會返回Object,Object對象是頂級對象。
在這裏插入圖片描述
這樣的話,是不是有人還想試試Object.prototype.proto?那就試試唄。
在這裏插入圖片描述
返回null,這就解釋了爲什麼Object是頂級對象,再往上找就沒有了。

原型鏈

講完原型,終於可以說原型鏈了。接下來原型鏈就在原型的基礎上就稍稍的簡單了一點。
看圖看圖。
在這裏插入圖片描述
沒錯,就是看這個圖,有沒有看到對象的__proto__,用__proto__連接起來的就是原型鏈。當訪問到對象本身沒有的屬性或者方法時,此時就會順着原型鏈進行查找,一直找到Object,prototype,如果找到的話就調用返回,如果沒有找到就會返回undefined。
還是這個例子。

		function Person(uname, uage) {
            this.uname = uname;
            this.uage = uage;
        }
        // 構造函數也是函數, 所以構造函數也有自己的原型對象 
        var obj = new Person("xz", 18);

假如現在訪問sex屬性。肯定返回undefined,整個原型鏈上都沒有sex屬性。

console.log(obj.sex);

我在Perso.prototype上添加sex屬性後,再去訪問。此時就可以訪問到了,obj對象本身沒有這個屬性,但是obj的原型Peroson.prototype上有,obj通過__proto__屬性訪問Peroson.prototype得到sex屬性,最後返回。

Person.prototype.sex = "male";

在這裏插入圖片描述
同理,不在Peroson.prototype上添加sex屬性,在Object.prototype上添加。同樣的obj依然可以訪問到,通過原型鏈中__proto__屬性。
在這裏插入圖片描述
當obj對象、obj對象的原型以及Object原型對象都有sex屬性時,obj對象會返回哪個值?

		Person.prototype.sex = "male";
        Object.prototype.sex = "女";

        function Person(uname, uage, sex) {
            this.uname = uname;
            this.uage = uage;
            this.sex = usex;
        }
        // 構造函數也是函數, 所以構造函數也有自己的原型對象 
        var obj = new Person("xz", 18, "男");
        console.log(obj.sex);

返回的是obj對象自己的屬性值。
在這裏插入圖片描述
需要注意的是,先從自己找,自己沒有就找自己的原型,原型沒有就再往上以及原型找,就這樣一層一層的找。

JavaScript查找機制:就近原則。

補充

這種形式是在原型對象上追加屬性

		Person.prototype.sex = "male";

這種形式是改寫了原型對象,也就是說,這個把系統提供的Person.prototype給覆蓋了。再訪問Person.prototype時只有sex這一個屬性了。

        Person.prototype = {
            sex: "male"
        }

在這裏插入圖片描述
覆蓋的問題要謹慎。

emmm,結束了結束了。原型和原型鏈暫時就瞭解了這些哈。

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