---
title: 'JS 核心 21 - 繼承與原型介紹'
tags: JS 核心 ,JS , JavaScript
description: 2021/02/18
---
JS 核心 -- 繼承與原型介紹
===
## 原型鍊的概念 - 為什麼有原型
### 程式語言 JavaScript 名稱由來
1. 程式語言 JavaScript 與 Java 名字很接近,但寫法差距很大。
2. 在推出 JavaScript 時基於商業考量,希望能夠吸引 Java 開發者投入 JavaScript 的開發,所以將程式語言名稱取為 JavaScript。
3. avaScript 的特性也是源自於 Java,就是使用 new 方法來新增物件。Java 開發者看到可使用 new 方法來新增物件會覺得兩者看起來很相近,但兩者的根本運作有很大的不同。
4. 透過 new 方法所新增物件會有繼承的特性,兩者在繼承實作方式上有很大的不同。
5. 在 Java 裡面是屬於 <span class="red">**類別繼承**</span>。
6. 在 JavaScript 裡面是屬於 <span class="red">**原型繼承**</span>。
### Java <span class="red">**類別繼承**</span> (物件導向)
> class 屬於 Java 物件導向的概念
1. 當需要定義狗,且數量不只一隻,要重複定義時,就會定義一個類別 class。 (如下右圖)
2. 類別 class 名稱就是 "狗",在 "狗" 之下會定義狗應有的一些屬性及方法。
3. 可以透過類別 class 定義更多的狗出來。

### JavaScript 使用 <span class="red">**原型繼承**</span>
>「原型」繼承可以讓本來沒有某個屬性的物件去存取其他物件的屬性。
> 繼承表示一個物件取用另一個物件的屬性或方法。
> JavaScript 中的所有物件都有原型屬性,這個屬性會參考到另一個物件,我們稱為原型 proto(被參考的物件)。
( 如上左圖示 )
* class : 想像中的狗映像,沒辦法應用,只是想法
* instance 實體: 透過 new 方法,把想法實體化,可以定義特徵 (ex 色彩、體型、吼叫),實體的狗由狗的思想延伸出來的,這就稱為繼承。
* <span class="red">**繼承 : 一個實體可以取用另一個物件的屬性及方法**</span>
* 所有的狗都有色彩、大小,繼承了此**屬性**
* 所有的狗都會叫,繼承了此**方法**
> 透過原型的方式做出類似 class 繼承的方法
* JavaScript 特性 : 根本都是物件。任何內容都是以物件的方式做建立。
* JavaScript 並沒有 class 概念。所以必須以原型的方式做出類似 class 的方法。
---
## 原型在哪裡?
使用**物件**的方式來定義一隻狗。
若狗要建立實體,透過此原型物件來繼承原有的屬性和方法。(兩個物件的概念)

在運行 JavaScript 時,當建立物件實體就有它自己的屬性和方法。
### 原型的特性
* 實體物件有自己的屬性 ; 原型物件也有自己的屬性 (結構上都是使用物件)。
* 實體可以繼承一個原型,原型可以繼承另一個原型,另外一個原型也有自己的屬性和方法。
* 原型鍊 : 繼承可以一段一段向上繼承。
* 在原本的實體內取不到屬性,透過原型鍊方式不斷向上查找,直到原型鍊頂端為止。
* 若從原型新增兩個實體,兩個實體會共用同一個原型的方法。

### :pencil2: 範例 : 顯示陣列裡的最後一個值
1. 「實體物件」可以透過「點運算子」來取用**自己**的屬性及方法
* 也可透過「點運算子」不斷向上查找來取用**原型**的屬性及方法
2. 使用 forEach() 方法 : 可以把陣列裡所有的值一一取出並執行
* forEach() 方法不是 a 實體原本的屬性; 是「陣列原型」裡面的屬性
3. a、b 兩陣列實體都同時繼承「陣列原型」下
* 原型是共用的,_ _proto_ _ 連結的是陣列的原型,可透過此方式在「陣列原型」上新增內容
```
var a = [1, 2, 3]; // a 為一實體陣列 (也屬於物件) (陣列的本質就是物件)
console.log(a, a[1], a.length); // 結果如下左圖
a.forEach(function(i) { // 取用原型方法 forEach 把 a 陣列裡的值一一取出並執行
console.log(i); // 1 2 3
});
var b = [4, 5, 6];
a.__proto__.getLast = function () { // 透過 a.__proto__ 的方式在原型鏈上新增方法 (如下右圖)
return this[this.length -1]; // this 代表此陣列。將陣列的最後一個數字取出
// 類似 return b[b.length -1];
}
console.log(a, b); // [1, 2, 3] [4, 5, 6]
console.log(a.getLast(), b.getLast()); // 3 6 (有共用的原型方法)
```

* <span class="red">實際開發上**不建議直接對 _ _proto_ _ 新增功能**</span>
* 使用此方法很容易發生汙染,也會不知道該方法是屬於哪個功能底下
* 實際開發上建議使用 prototype
### 陣列原型又繼承物件的原型 (有屬於自己的方法及內容)
> 原型是有多個層級的,可以不斷向上查找
> <span class="red">**物件原型沒有 _ _proto_ _**</span> ,因為他是最頂層的物件原型內容
```typescript=
var a = [1, 2, 3];
var b = [4, 5, 6];
a.__proto__.getLast = function () { // 透過 a.__proto__ 的方式在原型鏈上新增方法
return this[this.length -1]; // this 代表此陣列。將陣列的最後一個數字取出
}
console.log(a, b); // [1, 2, 3] [4, 5, 6]
console.log(a.getLast(), b.getLast()); // 3 6
var family = { // 新增另一個物件實體
name: '小明家'
}
family.__proto__.getName = function() { // 在 family 物件的原型下新增 getName 方法
return this.name;
}
console.log(family, family.getName()); // {name: "小明家"} 小明家 (物件展開如下圖)
```
(第 3 行) 在陣列原型下新增 getLast 方法
(第 12 行) 在物件原型下新增 getName 方法 (如下左圖)
(第 6 行) 原型就是不斷向上查找的概念,打開陣列實體,再打開陣列原型,再打開物件原型,就可看到有 getName 方法 (如下右圖)

:pencil2: **試著在陣列裡面使用 getName 方法**
陣列的本質就是物件,可以在陣列上新增屬性 name
```typescript=16
b.name = '陣列的屬性'; // 陣列的本質就是物件,在陣列上新增一個屬性 name
console.log(b.getName()); // 陣列的屬性 (源自於物件原型裡的 getName 方法)
console.log(b.toString()); // 4 5 6 (使用物件原型下的方法 toString)
```
1. **原型**有不斷向上查找的特性,所以 b 可使用物件原型的方法 getName
2. a、b 兩「陣列實體」都同時「繼承陣列原型」下
* 此「陣列實體」除了可以用自己原本的屬性外,也可用陣列原型內的所有方法
* 原型有向上查找的特性,「陣列原型」又繼承於「物件原型」之下
* b 屬於「陣列原型」下的「陣列實體」,依然可以使用「物件原型」下的方法
## :memo: 學習回顧
:::info
* Java 使用類別繼承。
* class 屬於 Java 物件導向的概念。
* JavaScript 使用原型繼承。
* 「原型」繼承可以讓本來沒有某個屬性的物件去存取其他物件的屬性。
* 繼承表示一個物件取用另一個物件的屬性或方法。
* 在繼承的行為裡,透過被繼承的「後代類別」,所產生出來的物件,一開始就應該要直接具有「前代類別」的屬性跟方法。
* JavaScript 中的所有物件都有原型屬性,這個屬性會參考到另一個物件,我們稱為原型 proto(被參考的物件)。
* JavaScript 透過原型的方式做出類似 class 繼承的方法
* JavaScript 特性 : 根本都是物件。任何內容都是以物件的方式做建立。
* JavaScript 並沒有 class 概念。所以必須以原型的方式做出類似 class 的方法。
* 原型是什麼? 一種讓別的物件繼承其中的屬性的物件。
### 原型的特性
1. 一樣具有物件的特性。
* 實體物件有自己的屬性 ; 原型物件也有自己的屬性 (結構上都是使用物件)。
2. 原型鍊 : 繼承可以一段一段向上繼承。
* 實體可以繼承一個原型,原型可以繼承另一個原型,另外一個原型也有自己的屬性和方法。
3. 向上查找的特性
* 在原本的實體內取不到屬性,透過原型鍊方式不斷向上查找,直到原型鍊頂端為止。
4. 原型可共用方法及屬性
* 若從原型新增兩個實體,兩個實體會共用同一個原型的方法。
* 原型是共用的,_ proto _ 連結的是陣列的原型
---
* 「實體物件」可以透過「點運算子」來取用自己的屬性及方法
* 也可透過「點運算子」不斷向上查找來取用原型的屬性及方法
* 物件原型沒有 _ proto _ ,因為他是最頂層的物件原型內容
:::
## :+1: 相關參考文件
:::info
[原型(Prototype)](https://ithelp.ithome.com.tw/articles/10205313)
[KURO -物件與原型鏈](https://ithelp.ithome.com.tw/articles/10194154)
[物件導向與原型繼承](https://huangpei.medium.com/%E5%85%8B%E6%9C%8Djs%E5%A5%87%E6%80%AA%E7%9A%84%E9%83%A8%E5%88%86-%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91%E8%88%87%E5%8E%9F%E5%9E%8B%E7%B9%BC%E6%89%BF-4edf1d5c9024)
[該來理解 JavaScript 的原型鍊了](https://blog.techbridge.cc/2017/04/22/javascript-prototype/)
:::
<style>
.red {
color: red;
}
.green {
color: green;
}
</style>