# Prototype 原型、Prototype Chain 原型鍊、Prototype Inheritance 原型繼承 ## Prototype(原型), Prototype Chain (原型鍊) ### `__proto__` > 讓我們 access 一個物件的 prototype *(記得 prototype 實際上也是一個 object)* :::info 所有的 object 都有 `__proto__` 這個 property ::: *一個 object (物件) 裡面,除了所給予的 property (屬性值) 外,另外也包含 prototype (原型)。* *** ### JS 中任何東西的 prototype 到最後都是物件(除了原始型別) ::: warning **Everything is an object** (or primitive) ::: <img src="https://i.imgur.com/gFUA8ep.png" width="500px"> #### object 物件 ```javascript const a = {}; ``` <img src="https://i.imgur.com/ZccqkGu.png" width="300px"> <br > #### function 函式 ```javascript const b = function(){}; ``` <img src="https://i.imgur.com/TdR4QjI.png" width="250px"> ![]() <img src="https://i.imgur.com/BCzDBH5.png" width="300px"> #### array 陣列 ```javascript const c = []; ``` <img src="https://i.imgur.com/QvSx4he.png" width="250px"> <img src="https://i.imgur.com/zAFeEvl.png" width="250px"> <img src="https://i.imgur.com/oCLMzXS.png" width="300px"> *** ### 補充:primitive type 原始型別 > #### NOT AN OBJECT > A type of data that represent a **single value** | primitive type | | -------- | | String | |Number| |Boolean| |`undefined`| |`null`| |BigInt| |Symbol| ### MDN :point_down: ![](https://i.imgur.com/0h2Qo7r.png) *** ### Prototype Chain (原型鍊) 當我們在一個 Object 上 access 一個 property/method 時 ,如果在該 Object 上找不到,JavaScript Engine 就會查看該 Object 的 prototype(另一個 object),找不到再查看這個 prototype 的 prototype(再另一個 object)... 就這樣一直找下去,直到找到 property/method,或是到達了原型鏈(prototype chain)的終點。 <img src="https://i.imgur.com/Vydwlm4.png" width="500px"> *我們不需要做 `obj.__proto__.__proto__.prop3`, JS Engine 因為有 prototype chain 的行為,`obj.prop3`能直接用幫我們找到* > #### JS Engine 這個沿著 prototype 一路往上查找的行為,就是 prototype chain。 <br > *** ## Building Objects :::success > ### 用 prototype 建立物件的三種方式 - **Function Constructors** - **Classes** - **`Object.create()`** ::: #### 複習:function constructors & `new` ```javascript= function Person(firstName, lastName){ console.log(this); this.firstName = firstName; this.lastName = lastName; console.log('This function is invoked') } const john = new Person('John', 'Doe'); console.log(john); // Person {} // This function is invoked // Person {firstName: "John", lastName: "Doe"} ``` <img src="https://i.imgur.com/v9F0Sek.png" width="600px"> <br > ### `.prototype` > #### function 這種 object 的內建 property (屬性) > 用來設定由這個 function constructor 建立的 object 的 prototype <img src="https://i.imgur.com/pe5xY4V.png" width="500px"> :::warning - 與 `__proto__` 是完全不同的東西 - `.prototype` 不是這個 function 的 prototype - **只有一個 function 被當做 function constructor,透過 `new` 這個關鍵字來執行這個 function 時,function 的 `.prototype` 才有意義** ::: <img src="https://i.imgur.com/ZSdlWLi.png" width="800px"> 因為 Prototype Chain 的概念,所以我們可以用 `.prototype` 這樣的方式去設定由 function constructor 建立的 object 的 method ```javascript= function Person(firstName, lastName){ this.firstName = firstName; this.lastName = lastName; } Person.prototype.getFullName = function(){ return this.firstName + ' ' + this.lastName; } Person.prototype.getFormalFullName = function(){ return this.firstName + ', ' + this.lastName; } const john = new Person('John', 'Doe'); const jane = new Person('Jane', 'Doe'); console.log(john.getFullName()); // John Doe console.log(jane.getFormalFullName()); // Jane, Doe ``` <br > *** ### `Object.create()` > #### Pure Prototypal Inheritance 最單純的原型繼承 ```javascript= const person = { firstName: 'Default', lastName: 'Default', greet: function () { return `Hi, ${this.firstName} ${this.lastName}`; } } const john = Object.create(person); console.log(john); ``` **注意`this` 的使用** :::info 透過 `Object.create()` 可以 1. 建立一個 empty object 空物件 2. 將帶入 `Object.create()` 的參數內容,變成該物件的prototype 原型。 ::: <img src="https://i.imgur.com/H2PGhcp.png" width="500px"> ![]() :point_up: 產生一個空物件 <br > <img src="https://i.imgur.com/vBR2v1C.png" width="400px"> :point_up: prototype 指向傳入 `Object.create()` 的參數 <br > #### 用 `Object.create()` 製造 object ```javascript= const person = { firstName: 'Default', lastName: 'Default', greet: function () { return `Hi, ${this.firstName} ${this.lastName}`; } } const john = Object.create(person); john.firstName = 'John'; console.log(john.greet()); // Hi, John Default ``` > 基於 **prototype chain** 的觀念 > overwrite 作為 prototype 的 object (base object) 的 property 對於 `firstName` 來說,在該物件就已經有這個屬性,因此它不會在往該物件的 prototype (原型) 去尋找,而對 `greet` 與`lastName` 來說,因為在 `john` 這個物件裡沒有這個 method 與這個 property,於是就會到 prototype 裡面去找 *這個方法更接近 JavaScript 的 prototype inheritance 運作原理,**所有的 prototype 也都是 object**,由 object 來繼承 object*