Web開發學習筆記08 — prototype、__proto__、constructor、Prototype Chain(原型鏈)


Posted by Teagan Hsu on 2020-12-09

Prototype & _proto_ (dunder proto)

prototype(原型)是真實的物件,後面可以接方法(methods)。
__proto__是物件的內部屬性,指向其原型。

那他們之間的關係是?

以下範例來看,game1game2__proto__就是指向pcGame.prototype。JavaScript透過__proto__去找到pcGame.prototype,發現裡面有rate這個方法。

function pcGame(name, genre){
this.name = name;
this.genre = genre;
this.score = null;};

pcGame.prototype.rate = function(score){
this.score = score;
}

let game1 = new pcGame('Age of Empires 3', 'Real-time strategy');
let game2 = new pcGame('The Outer Worlds', 'Action role-playing');

game1.__proto__ === pcGame.prototype;
game2.__proto__ === pcGame.prototype;

//true
//true

如果找不到方法的話,JavaScript就會往pcGame.prototype的上一層pcGame.prototype.__proto__繼續找,直到找到null才會停止。

pcGame.prototype.__proto__ === Object.prototype
pcGame.prototype.__proto__.__proto__ === Object.prototype.__proto__
Object.prototype.__proto__ === null
//true
//true
//true

Prototype Chain

►像上述那種一層層串起來的就是Prototype Chain。

以下範例,創建一個函式pcGame,我們可以用new去取用函式rate,但會發現game1game2使用的函式rate不是同一個,而是重複建立出來的。

function pcGame(name, genre){
this.name = name;
this.genre = genre;
this.rate = function(score){
this.score = score;};

let game1 = new pcGame('Age of Empires 3', 'Real-time strategy');
let game2 = new pcGame('The Outer Worlds', 'Action role-playing');

game1.rate === game2.rate;
//false

要改善這點,我們可以把函式rate放在pcGameproperty裡,也就是pcGame的原型,這樣就不用每次都建立新的方法,而是可以共用同一個方法。

function pcGame(name, genre){
this.name = name;
this.genre = genre;
this.score = null;};

pcGame.prototype.rate = function(score){
this.score = score;
}

let game1 = new pcGame('Age of Empires 3', 'Real-time strategy');
let game2 = new pcGame('The Outer Worlds', 'Action role-playing');

game1.rate === game2.rate;
//true

Constructor

new一個物件時會執行的函式。

constructor是一個函式,它創立一個物件的繼承(instance)。在JavaScript中,當我們使用new關鍵字去宣吿(declare)一個物件時,將呼叫constructor。換句話說,只要使用new去宣告物件後呼叫的函式,我們就可以看做是constructor function

呼叫constructor時發生了什麼?

①使用new創立一個新的物件。
tihs指向我們新new出來的物件。
③將物件的__proto__指向constructorprototype,形成原型鏈。
④回傳物件。


模型圖片

這邊繪製了整個關係的圖,可以更好理解運作:


如何檢查屬性是不是屬於當前物件?

有三種方式可以檢查:
1. hasOwnProperty()
2. instanceof
3. isPrototypeOf()

hasOwnProperty()

語法

obj.hasOwnProperty(prop)

按照前面給的程式範例來驗證看看,發現確實rate不是game1原本就有的屬性,而是存在於原型鏈中。

game1.hasOwnProperty('name');
//true

game1.hasOwnProperty('rate');
//false

hasOwnProperty()的限制

hasOwnProperty()無法檢查整條原型鏈,它只會檢查輸入的物件。要改善這點,可以利用for in來遍歷檢查。

function pcGame(name, genre){
this.name = name;
this.genre = genre;
this.score = null;};

pcGame.prototype.rate = function(score){
this.score = score;}

let game1 = new pcGame('Age of Empires 3', 'Real-time strategy');
let game2 = new pcGame('The Outer Worlds', 'Action role-playing');

for(let i in game1){
if(game1.hasOwnProperty(i)){
console.log(`自身屬性 ${i}`)}else
console.log(`繼承自其他屬性 ${i}`)
}

//自身屬性 name
//自身屬性 genre
//自身屬性 score
//繼承自其他屬性 rate

instanceof

instanceof用於檢查constructorprototype屬性有沒有出現在該物件的原型鏈上。
語法:

object instanceof constructor

實例:

game1 instanceof pcGame;  //true
game2 instanceof pcGame;  //true
game1 instanceof Object;  //true
game2 instanceof Object;  //true
pcGame instanceof Object;  //true
pcGame instanceof Function;  //true

game1 instanceof Function;  //false
game2 instanceof Function;  //false

因為game1繼承的是pcGame.prototype。所以game1是繼承Object而不是Function

game1.__proto__ === pcGame.prototype;  //true
pcGame.prototype.__proto__ === Object.prototype;  //true
Object.prototype.__proto__ === null;  //true

isPrototypeOf()

isPrototypeOf()是一個方法(method),可以幫忙檢查物件有沒有存在於該物件的原型鏈上。
語法:

prototypeObj.isPrototypeOf(object)

實例:

pcGame.prototype.isPrototypeOf(game1);  //true
pcGame.prototype.isPrototypeOf(game2);  //true

參考資料:

  1. proto VS. prototype in JavaScript
  2. 你懂 JavaScript 嗎?#19 原型(Prototype)
  3. 該來理解 JavaScript 的原型鍊了
  4. What is a constructor in JavaScript?
  5. [筆記] 了解JavaScript中原型(prototype)、原型鍊(prototype chain)和繼承(inheritance)的概念
  6. instanceof - MDN
  7. Object.prototype.isPrototypeOf() - MDN
  8. Object.prototype.hasOwnProperty() - MDN
  9. js中的hasOwnProperty()和isPrototypeOf()

#prototype #constructor #__proto__ #Prototype Chain #hasOwnProperty() #isPrototypeOf() #instanceof







Related Posts

JS 設定預設值 (Default Parameters)

JS 設定預設值 (Default Parameters)

C++教學(三) 運算

C++教學(三) 運算

Git 版本控制(下)

Git 版本控制(下)


Comments