iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
自我挑戰組

登堂入室!前端工程師的觀念技術 _30_ 題系列 第 7

6. Prototypal inheritance 的運作原理

(這篇會延續Constructor Function的內容,來解釋 Prototype 和 Prototypal inheritance。)

(想看結論可以看粗體跟移到最下面。)


4. 關於 Constructor Function 解釋建構子函式的文章時,有提到把屬性(properties)和方法(methods)都丟進建構子,這種建立物件的方式會占用到大量的記憶體。

我們可以先看看這個例子:

function Person(name, age, country){ 

    this.name = name,
    this.age = age,
    this.country = country,

    this.sayHi = function(){
    console.log(this.name + " says Hi.");  
    }
}

let Tina = new Person("Tina", 22, "Taiwanese");  
let Winnie = new Person("Winnie", 108, "Japanese");  

console.log(Tina.sayHi === Winnie.sayHi); // output: false

Tina.sayHiWinnie.sayHi的比較結果是不相等,要怎麼解釋這個結果呢?

也就是說,即使他們定義的method一模一樣,也會被當成不同的物件 → 指向不同的記憶體位置
那麼, 假設今天要製造一百個物件,就會製造一百個sayHi() → 占用一百個記憶體位置

這時侯我們可以利用prototype:

function Person(name, age, height){
    this.name = name,
    this.age = age,
  this.country = country
}

Person.prototype.sayHi = function() {  // 在Person的prototype屬性定義sayHi()
    console.log(this.name + " says Hi");
}

let Tina = new Person("Tina", 22, "Taiwanese");  
let Winnie = new Person("Winnie", 108, "Japanese");  

console.log(Tina.sayHi === Winnie.sayHi); // output: true

從這裡可以知道,Tina.sayHiWinnie.sayHi兩個函式嚴格相等(指向同樣的記憶體位置),這樣就可以解決佔用記憶體的災難。

那接下來要來了解,為什麼加入prototype可以解決這個問題?

所以我們要來認識JS的繼承(inheritance)和原型鍊(Prototype chain):

prototypal inheritance 原型繼承


  • 在JS裡,所有的物件都有一個隱藏屬性Prototype(原型),而prototype本身也是一個object(物件)。
  • prototype可能為null,或參考至其他物件。

In JavaScript, objects have a special hidden property [[Prototype]] (as named in the specification), that is either null or references another object. That object is called “a prototype”:

當我們需要讀取一個物件的屬性,卻找不到時,JS會自動從從該物件的prototype查找。

在程式語言中,這個行為被稱作 「原型繼承」。 (← 全文重點

When we read a property from object, and it’s missing, JavaScript automatically takes it from the prototype. In programming, this is called “prototypal inheritance”.

從我們剛剛執行的例子,我們可以執行console.log(Tina);來瞭解他們的階層關係:
https://ithelp.ithome.com.tw/upload/images/20210907/201294762znVkaYpjJ.png

  1. 物件Tina本身是建構子Person創造出來的實例(instance)。
  2. 最下方有個名為[[prototype]]的屬性,展開可以看到建構子為Person()
  3. 再展開,最下方還是有個[[prototype]]屬性,可以再看到建構子為Object()

由此可知,他們的繼承(查找)機制是這樣子:

Tina ---> Person.prototype(紅色框) ---> Object.prototype(綠色框) ---> Null

(JS 使用 constructor function 來定義物件的屬性與方法,而產生出的新物件被稱為實例(Instance)。)


最後補充一下MDN的定義:

講到繼承,JavaScript 就只有一個建構子:物件。
每個物件都有一個連著其他原型(prototype)的私有屬性(private property)物件。 原型物件也有著自己的原型,於是原型物件就這樣鏈結,直到撞見 null 為止:null 在定義裡沒有原型、也是原型鏈(prototype chain)的最後一個鏈結。
引用自 繼承與原型鏈 - JavaScript | MDN

做個結論:

→ 每個JS裡的物件(object),都繼承(inheritance)自該物件的原型(Prototype),並保留其屬性和方法。

→ 使用建構子函式產生實例以後,這些被產生的物件之間的連結被稱為原型鍊(Prototype chain)。

→ 當我們需要讀取一個物件的屬性,卻找不到時,JS會自動從從該物件的prototype查找,也就是原型繼承(prototypal inheritance)。

參考資料:

【如內文有誤還請不吝指教>< 謝謝閱覽至此的各位:D】

-----正文結束-----


上一篇
5. bind, call, apply 的差異
下一篇
7. 解釋 Event Loop ( 上 ) --- Call Stack
系列文
登堂入室!前端工程師的觀念技術 _30_ 題31

尚未有邦友留言

立即登入留言