# Q1 - Prototype Chain and Class Inheritance
<br>
Outline :
- 其他語言的繼承( Classical inheritance )
- JS 的繼承 ( Prototypal inheritance )
- 原型鏈(Prototype Chain)
- 使用原型繼承的好處
- ES6 中的 class 語法糖
## 前言:簡單了解「其他語言」的繼承
所謂的繼承,簡要而言,就是**一個物件可以將自己的屬性(property)或方法(method),提供給其他物件使用 ; 或者反過來說,一個物件的屬性(property)或方法(method)可以被其他物件提取**。
而提供屬性、方法給其他物件使用的原始物件,可以視為模組,也就是 class。
### Class (類別) 、Instance(實體)與 Classical inheritance
Class (類別) -> 規格書、模組的概念
以製作狗狗為例子,假設想要做各種類型的狗狗,這些狗狗有各自的毛色和體型,但全部都會叫,這時候,最原始的模組 class,可能會長這樣。

- 所有的狗都會執行叫這個動作的 func,這類「動態操作」稱之為「方法」。
- 每隻狗有不同的顏色和體型,這類「靜態屬性」稱之為「屬性」。
有了模組 class 後,搭配 new 命令式語法,能做出多種的 instance(實體對象)。

( Java 程式。)

所以很多隻的狗狗物件有自己的顏色和體型,也都擁有同樣從 class 中提取的叫方法,這樣就是種繼承。
這類型的繼承,就稱為 **classical inheritance**。
<br>
## 從 JS 緣起了解 JS 的原型繼承
Brendan Eich 最初在設計 JavaScript 時,數據類型都是對象 object (參考當時火紅的 object-oriented programming 語言),例如: Java。
But!他並沒有設計 class 的概念,可能是因當時只是想解決一些相對 server 而言不複雜的 client 端問題,不想太複雜或讓語言太困難。
然而還是必須要有一種機制,將所有 object 聯繫起來運作,因此最後還是設計了「 繼承 」的概念。
於是參考其他語言後,打算引入 new 命令式來從一個「原型對象(模型)」生成一個「實體對象 (intance)」。
But ! JS 中沒有「 class 」的設計,哪來原型對象?
因此做一個簡化的設計,由於 Java / C++ 等,在使用 new 時,都會調用 class 中的建構函式(constructor)。所以就 **利用 constructor 作為模型對象去 new 出 instance**。
```java=
// 「 Java 」 中, class 中有 constructor
// 在此其餘語法不是重點,注意第 7 行與第 21 行即可
class Human {
String name;
int age;
int height;
Human(String str,int a,int b){ // constructor 建構函式初始化用
name=str;
age=a;
height=b;
}
void eat(){
System.out.println("eating");
}
}
//end of class Human
class Test{
public static void main(String[] args){
Human h1 = new Human("小木",22,178); // new 時使用 constructor
}
}
// end of class Test
```
```javascript=
// 「 JS 」中函式可以做為 constructor 使用
// 利用 constructor 生成 instance
function DOG(name){
this.name = name
this.species = '犬'
}
const dogA = new DOG('肚子')
const dogB = new DOG('吐司')
console.log(dogA.name) // 肚子
console.log(dogB.name) // 吐司
console.log(dogA) // DOG {name: "肚子", species: "犬"}
console.log(dogB) // DOG {name: "吐司", species: "犬"}
// 無法共享「屬性(Property)」和「方法(Method)」
dogA.species = '貓'
console.log(dogB.species) // 犬 -> 不會受到 dogA 影響,無法共享屬性
```
But ! 用 constructor 生成 instance 是無法共享「屬性」和「方法」。
如此一來,雖然能達成非共享內容的繼承,卻沒辦法達成共享繼承,會導致一些麻煩,例如:記憶體問題、程式撰寫不方便等。
因此 Brendan Eich 決定設計一個 **prototype 屬性**,這個屬性會屬於一個對象(constructor),而所有由這個對象創建的 instance 需要共享 prototype 中的屬性和方法。
```javascript=
// constructor
function DOG(name){
this.name = name;
}
// constructor 中的 prototype
DOG.prototype = { species : '犬' }
// 生成 instance : dogA & dogB
const dogA = new DOG('肚子')
const dogB = new DOG('吐司')
console.log(dogA) // DOG {name: "肚子"}
console.log(dogB) // DOG {name: "吐司"}
console.log(dogA.species) // 犬
console.log(dogB.species) // 犬
```
- **需要共享**的屬性和方法,就放在 prototype (constructor.prototype)裡,prototype 是物件。
- **不需要共享**的屬性和方法,就放在 constructor 裡,constructor 是物件。
- instance 一創建,會自動引入 prototype 物件中的屬性和方法。
這正是所謂的「**原型繼承 prototypal inheritance**」,prototype 的對象 (DOG) 就像是 instance (dogA / dogB) 的原型,而 instance (dogA / dogB) 像是繼承 prototype 對象 (DOG) 一樣。
<br>
## 所以,DOG.prototype,存在於 instance 的哪裡?
把 instance (dogA / dogB) 印出來看看!找找 DOG.prototype 物件在哪。

會發現在 dogA / dogB 中,都有一個特別的 **_proto_ 物件**,這個物件中,就存在來自父層級繼承來的 prototype 。
更精確地說法是:**_proto_ 會在每個物件被生成時,自動被指派到該物件的屬性上,並會指向該物件的原型物件的 prototype**。
依照上例而言,dogA 物件在被 new 出來時,就有帶有 `_proto_` 屬性,並由於 `DOG.prototype = { species : '犬' }` ,所以 dogA._proto_ 就是 `{ species : '犬' }` 。
```javascript=
// 當我們使用 dogA.species 時,會先在自己的物件內尋找,發現只有 name 沒有 species 屬性 ,於是會再自動去尋找 dogA._proto_,發現有 species 屬性於是停止。
console.log(dogA.species) // 犬
console.log(dogA._proto_.species) // 犬
```

```javascript=
DOG.prototype === dogA._proto_ //true
```
## 小結
1. JavaScript 裡沒有 class 模型,是透過 new + constructor 建立 instance 們。
2. instance 們可以透過 constructor.prototype 的方式,共同繼承 prototype 物件中的屬性和方法。
3. _proto_ 存在於每個物件中,指向原型物件的 prototype。當呼叫物件的屬性或方法時,會先尋找物件本身有沒有,沒有的話就會再往物件的 _proto_ 去尋找。
---
## 繼承(Inheritance)
一個物件可以提取到其他物件中的屬性(property)或方法(method)。
繼承可以分成兩種,一種是 classical inheritance,這種方式用在 C# 或 JAVA 當中;另一種則是 JavaScript 所使用的,是屬於 prototypal inheritance。
## 原型鏈(Prototype Chain)
Javascript 使用的是 prototypal inheritance,所以必然會用到原型(prototype)的概念。
- prototype:會一直存在於建構函式(constructor)上的屬性,所有透過該函式產生的物件都有能力存取。
- _proto_:會在物件被生成時一起被指派到物件上的屬性,他決定這個物件的原型物件是誰。
---
## 如果一直往父層的父層的父層的...找下去?
```javascript=
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log(this.name + ', age:' + this.age);
}
var nick = new Person('nick', 18);
```
**最後都會找到原型鏈最底端的`Object`(原型物件)。**

---
Javascript 的純值、物件都有原型鏈,並透過原型鏈繼承屬性、方法:
```javascript=
var a = {};
var b = function() {};
var c = [];
```

---
如果需要的話,不同物件可以分享一樣的原型,它們共享同一個屬性但不是直接的,藉由原型鏈的概念,當 Javascript 搜尋 prop2 時會指向同一個位置。

舉個例子:
```javascript=
var person = {
firstname: "Default", // 屬性
lastname: "Default", // 屬性
getFullName: function() { // 方法
return this.firstname + " " + this.lastname;
}
};
var john = {
firstname: "John",
lastname: "Doe"
};
// 將 john proto 物件指向 person
john.__proto__ = person; // john 繼承自 person
// john 裡面找不到所以到 person 找
console.log(john.getFullName()); // John Doe
// 在 john 中找得到的屬性就不會到原型鏈上找
console.log(john.firstname); // John
```
圖示:

---
## 為何要將屬性和方法建立在原型物件上?
```javascript=
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
// 新增屬性和方法在 Person 的原型上
Person.prototype.getFullName = function() {
return this.firstname + " " + this.lastname;
};
var john = new Person("John", "Doe");
console.log(john.getFullName()); // John Doe
```
如果我們將所有的屬性和方法建立在函數中 `Person()`,倘若我們有 1000 個物件都透過 `new` 來建立時,就會有 1000 筆 `getFullName` 存在於記憶體當中,相當佔位置;但若建立在原型物件上,使物件在取用時透過原型鏈找到這屬性和方法,那麼就只需要存在記憶體中一次而不需要在每個物件裡面都建立。
---
## 小結
<!--  -->

* `prop1`:`obj` 本身就**有** `prop1` 這個屬性,直接取用。
* `prop2`:`obj` 本身**沒有** `prop2`,當找不到時會往該物件的 `_proto_` 裡面尋找。
* `prop3`:同理找不到時會往 `_proto_` 這個物件裡面的 `_proto_` 再找下去。
`prop3` 實際是透過 `obj > _proto_ > _proto_` 這樣往下找的,你可以 `obj._proto_._proto_.prop3` 這樣得到 `prop3`,不過其實只要 `obj.prop3` 即可,而這樣尋找下去的鏈即為原型鏈(prototype chain)。

> 雖然利用原型繼承相當方便且強大,但必須注意程式碼內原型鏈的長度、必要時打破它們,以避免潛在的效能問題。再來,除非要處理 JavaScript 新語法的相容性,否則絕對不能擴充原生的原型。
---
### JavaScript 中的(偽)Class Inheritance
由於 class 的用法在許多程式語言中都相當普遍,在 ES6 中也會添加了 Class 的用法,然而這種用法實際上和 classical inheritance 仍然是不同的!實際上只是 **syntax sugar**,在 JavaScript 中實踐繼承的方式依然是 prototypal inheritance 的方法。
class 的這種做法在 JavaScript ,只是用來建立物件和原型的另一種**更便利的撰寫方式**,但背後運作的邏輯其實和 function constructor 或者是 Object.create 都還是一樣的。
**可以視為:改用一種 class 的寫法實踐 Prototypal inheritance,這種寫法是抄 Class Inheritance 的寫法。**
```javascript=
///// class 語法糖寫法 /////
// 模組與創建 instance
class Person{
// 建構函式
constructor(name){ // 私有
this.name = name;
}
// 方法
getName(){
return "Hello " + this.name ; // 共有
}
}
// 建構 john instance
const john = new Person("John");
console.log(john); // Person { name: "John" }
console.log(john._proto_);// {constructor: ƒ, getName: ƒ}
```
```javascript=
///// prototype 寫法 /////
function Person(name) { // 私有
this.name = name;
}
Person.prototype = {
getName: function(){ // 共有
return "Hello " + this.name
}
}
// 建構 john instance
const john = new Person("John");
console.log(john); // Person { name: "John" }
console.log(john._proto_);// {constructor: ƒ, getName: ƒ}
```
好處是「模組間」的繼承很好寫也好理解:
```javascript=
// code from MDN
class Animal{
constructor(name){
this.name = name;
}
speak(){
return this.name + ' makes a bark';
}
sleap(){
return this.name + ' makes a sleep';
}
}
class Dog extends Animal{
speak(){
return this.name + ' makes a new bark';
}
}
const dogA = new Dog("肚子");
console.log(dogA);
dogA.speak(); // 肚子 makes a new bark
dogA.sleap(); // 肚子 makes a sleep
```
## 總結
- JavaScrpit 的繼承是藉由 constructor 與 constructor.prototype 實踐
- 原形鏈是物件使用方法或屬性時,會先尋找自身是否有,再尋找 _proto_ 物件是否有,一層一層找上去的過程。
- ES6 的 class 是語法糖
---
參考資料:
- [該來理解 JavaScript 的原型鍊了](https://blog.techbridge.cc/2017/04/22/javascript-prototype/)
- [Javascript继承机制的设计思想](http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html)
- [什么是语法糖?](https://www.zhihu.com/question/20651624)
- [Prototype chain-1](https://blog.techbridge.cc/2017/04/22/javascript-prototype/)
- [Prototype chain-2](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/prototype.html)
- [Prototype chain-3](https://pjchender.blogspot.com/2016/06/javascriptprototypeprototype.html)
- [class inheritance](https://pjchender.blogspot.com/2016/07/javascript-es6classes.html)
- [[筆記] 談談 JavaScript 中的 function constructor 和 prototype 的建立](https://pjchender.blogspot.com/2016/06/javascriptfunction-constructorprototype.html)
- [ [筆記] 談談 JavaScript 中的 function constructor 和關鍵字 new](https://pjchender.blogspot.com/2016/06/javascriptfunction-constructornew.html)
- [[筆記] 談談JavaScript ES6中的Classes](https://pjchender.blogspot.com/2016/07/javascript-es6classes.html)
- [初學者應知道的物件導向 JavaScript](https://developer.mozilla.org/zh-TW/docs/Learn/JavaScript/Objects/Object-oriented_JS)
- [Java 備用筆記](https://yubin551.gitbooks.io/java-note/objectConstructor.html)
**報告用投影片**
第一張投影片

第二張投影片
