OOP

Constructor Function

JS裡所有事物都是對象,function也是對象。
構造函數透過new operator 可以創建新的對象Object。如不使用 new 和一般函數沒有區別。

  • js 裡, new 是用來創建一個對象
  • new 構造函數(參數) -> 實例對象

基本 new 實現 思路

  1. 創建新的實例對象
  2. 實例對象proto 指向於 構造函數.prototype
  3. 構造函數裡 this 指向實例對象
  4. 返回實例對象

可以參考 ☞ 我寫了一個實現 new

Prototype 是什麼

JavaScript 基於 原型。
我們可以使用 prototype 來添加屬性/方法給所有有關實例對象都能共享。

Prototype Rules

必須牢記在心這些規則是在了解 prototype chain 基礎上

  • 每個函數都有 prototype 屬性
  • 每個對象內部屬性都有隱式 __proto__
  • 每個實例的 __proto__指向創建它的構造函數的 prototype
  • 每個函數是由 Function 所創建的。
  • Function.prototype === 一般函數.__proto__
  • 一般函數.prototype.__proto__ === Object.prototype

為何使用 Prototype 呢?

function Cat(name) {
 this.lives = 9;
 this.name = name;

 this.sayName = function () {
   console.log(`Meow! My name is ${this.name}`);
 };
}

const c1 = new Cat('cat1');
const c2 = new Cat('cat2');

c2.sayName = 'haha';
c1.sayName() // Meow! My name is cat1
c2.sayName() // haha

根據以上程式來講解,我把 c2.sayName 屬性值原函數改成字串haha。當 c1.sayName 還是一樣是函數。因為 c1c2 對象是不相等的,當然你改變某個對象的屬性是不會影響到另一個對象。如果你想要是這樣,其實是沒問題。

問題來了,我想要在我改某一個對象的屬性值時,另一個對象也需要立即得到反映。在這前提下,兩個對象必須是由 Cat構造函數所創建對象,如 c1c2 再回來這個問題 sayName 屬性的變更,如何共享其他對象了。

就是把 sayName 屬性存在 prototype 對象裡,這樣一來可以節省空間也可以讓所有實例對象共享(享用) prototype 對象所保存的屬性和方法,只要 prototype 對象裡屬性修改,可以立即在所有實例得到反映。稍微講解為何使用 prototype 可以節省空間呢。可以想像下,透過 new 來創建多個對象,多個對象裡屬性是一樣,這樣會佔用內存
當然我們可以分類哪個需要放在構造函數哪個需要放在 prototype

  • 具體某個對象的特定屬性,通常放在構造函數裡。例如 name, age等等,因為每個對象 name, age 有所不同,僅僅只有特定屬於某個對象。
  • 所有對象都可以共享屬性和方法,通常放在 prototype 對象裡。 例如 sayName 等等,因為所有對象可以共同訪問當前 sayName 的值,指是所有對象一個共同動作。

Prototype chain

  • Prototype chain 主要實現了繼承方法。
  • 每個prototype裡都有指向它自己prototype,這叫做 prototype chain
  • 每個構造函數都有一個屬性 prototype 對象,那麼 prototype 對象都包含一個 __proto__ 並指向創建它構造函數的 prototype (Object.prototype)。
  • 每個實例對象都有一個屬性 __proto__ 對象指向 prototype 對象(Object.prototype)。
function fn() {}
fn.prototype.__proto__ === Object.prototype // true

const obj = {}
obj.__proto__ === Object.prototype // true

Prototype & Prototype Chain

  • 為了節省空間,我們會把 methods 添加到 prototype 裡。
  • 全部對象都是由構造函數創建。
  • 每個函數都會有 prototype,當函數使用new時被作為構造函數使用,返回一個對象。
  • 每個對象裡都有隱藏式的 __proto__ 來指向構造函數的 prototype
  • 每個 prototype 裡都有指向它自己 prototype,這叫做 prototype chain

Prototype chain 裡如何尋找您的 methods or properties?

  1. JavaScript engine 會優先在對象裡找自有屬性
  2. 如果沒有找不到,將會進入構造函數裡prototype裡尋找到匹配屬性名。
  3. 屬性沒有在 prototype 裡,JS engine 會根據 chain 往下找。
  4. 通過 prototype chain 實現繼承情況下,尋找過程就會沿著 prototype chain 繼續找。
  5. 直到 chain 結束,就是根 Object,會返回 undefined

總結

Inheritance in JavaScript is when an object is based on another object. Inheritance allows us to reuse existing code, having objects take on properties of other objects.

When a function is called as a constructor using the new operator, the function creates and returns a new object. This object is secretly linked to its constructor’s prototype, which is just another object. Using this secret link allows an object to access the prototype's properties and methods as if it were its own. If JavaScript does not find a particular property within an object, it will keep looking up the prototype chain, eventually reaching Object() (top-level parent) if necessary.

We also looked at a few methods and properties that allow use to check the origins and references of objects and their prototypes, namely:

  • hasOwnProperty()
  • isPrototypeOf()
  • Object.getPrototypeOf()
  • .constructor