一文搞懂原型和原型鏈

2023-04-01 06:00:54

在瞭解原型和原型鏈之前首先得明確它倆是什麼東西:

原型:prototype
又稱顯示原型
1、原型是一個普通物件
2、只有建構函式才具備該屬性
3、公有屬性可操作

隱式原型:__proto__
1、只有物件(普通物件、函數物件)具備
2、私有的物件屬性,不可操作

有了上面的概念之後,我們再來探討一下什麼是原型和原型鏈。

原型(顯示原型) : prototype

我們定義一個字串變數的時候,該字串本身是不具備任何方法的,但是可以呼叫字串方法。

let str = 'hello'  // new String()
console.log(str);
console.log(str.length);

其實我們在定義一個字串變數的時候,隱式的範例化了new String()這個建構函式,所以我們才可以使用字串方法。

 console.log(new String());

這個length就是String的原型方法ptototype,字串本身有沒有這個方法不重要,字串的原型上有個方法就可以了。
原型的本質是一個普通物件,所以我們可以利用物件.屬性的方式呼叫方法。
如果我們用字串呼叫一個DCodes(),該方法在字串屬性上沒有並不存在,呼叫該方法會報錯。

我們給String的原型新增一個DCodes方法,字串就可以呼叫該方法了。

String.prototype.DCodes = function(){
       console.log('你好DCodes');
}
str.DCodes() // 你好DCodes

利用原型可以幹什麼呢?上面也說了,建構函式才具備原型,我們建立一個建構函式,可以通過範例化這個建構函式來呼叫原型方法和原型屬性。

// 建構函式
function Person(){
         this.name = '東方不敗'
}
let per = new Person()
console.log(per);


原型的本質是一個物件,那麼給Person這個原型新增一個方法

function Person(){
         this.name = '東方不敗'
}
Person.prototype.sum = function(a,b){return a + b}
let per = new Person()
console.log(per);
console.log(per.sum(1,2));


建構函式記錄了當前原型物件產生的歸屬,原型是基於那個建構函式構建的,那麼constructor指向的就是那個建構函式,這裡的constructor指向的就是Person()函數。


__proto__隱式原型

隱式原型只有物件(普通物件、函數物件)才具備,並且隱式原型是一個私有的物件屬性,不可操作。
上面也提到過,我們定義了一個字串,實際上是隱式的new String()String()的原型上有length,所以字串可以呼叫length方法,顯示原型prototype是建構函式才具備的,普通物件是沒有的,那麼普通物件是怎麼呼叫建構函式的原型方法的呢?答案就是普通物件具有隱式原型,隱式原型全等於顯示原型

let hello = 'hello'
console.log(hello.__proto__ === String.prototype);  // true


也就是說,普通物件的隱式原型__proto__等於建構函式的顯示原型prototype,普通物件就可以呼叫建構函式的原型方法。
谷歌瀏覽器中,隱式原型__proto__的寫法為: [[Prototype]]

到這裡就構成了原型鏈,用字串呼叫字串方法的時候,字串會在__proto__尋找對應的字串方法,__proto__等於prototype,也就是String()建構函式,如果String()的建構函式沒有該方法,那麼String()會繼續向上尋找,原型prototype是一個物件,那麼物件就會有隱式原型__proto__String()的隱式原型__proto__Object(),然後會在Object()的原型prototype上尋找,如果Object()的原型prototype上不存在該屬性,那麼就會通過隱式原型__proto__繼續向上尋找,直到找到對應的方法為止,如果沒有找到,那麼就會報錯,該方法不存在。(這一段需要好好理解)
這樣向上尋找,最終總會有盡頭,萬物的原型終點是誰呢?
字串、陣列、建構函式的原型最終都會指向Object,而Object的原型指向的是null

console.log(Object.prototype);

最後我們來看一下prototype、__proto__之間的關係:

__proto__ === prototype 
prototype == {}
{}.__proto__ == Object.prototype
......