---
title: 'JS 核心 22 - 使用建構式自定義原型'
tags: JS 核心 ,JS , JavaScript
description: 2021/02/18
---
JS 核心 -- 使用建構式自定義原型
===
## 如何定義自己的原型
> <span class="red">**使用建構式的方式產生物件,並且會繼承同一個原型。**</span>
> 用建構函式,搭配 new 關鍵字的使用,以一個固定的藍圖建立許多結構相同的物件
**先用一個函式(建構式)來做為他們的藍圖**
* 藍圖無法變成實體 (instance)
* 若要將藍圖變成實體要透過 new 這個運算子
* 這兩隻狗是共用一個藍圖產生的 : (指 Dog 建構函式)
兩隻狗有各自的屬性,有一個共用的方法 bark (吠叫),在吼叫時會帶上自己的名字
### new 運算子
* 建立一個新的物件,並且連結回原本的建構物件 (指 Dog 函式)
* 將物件的 _ proto _ 指向建構子的 prototype,形成原型串鏈
* 會把新產生物件的 this 綁定在函式之上 <span class="red">**(在建構函式內使用的 this 就會綁定在新物件上)**</span>
* 將建構子的 this 指向 new 出來的新物件
* 回傳這個物件
### Dog 就是一個構造函數,可以用 new 這個關鍵字 new 出一個 instance 來。
* <span class="red">透過 「new 運算子」搭配「Dog 建構函式」來產生新的物件「狗的實體」,新的物件和原本物件沒有關聯性</span>
```typescript=
// constructor 建構函式 (上面兩隻狗的藍圖) (只準備了屬性)
function Dog(name, color, size) {
this.name = name; // 使用 this 將這些屬性綁定在他們自己身上
this.color = color;
this.size = size;
}
// 兩隻狗都是由 Dog 這個建構函式所產生出來的
var Bibi = new Dog('比比', '棕色', '小'); // 每次產生的物件也都會綁定在函式的 this 上
console.log(Bibi); // Dog{name: "比比", color: "棕色", size: "小"} (如下左圖)
var Pupu = new Dog('噗噗', '白', '大'); // 透過此藍圖再來產生一隻狗
console.dir(Dog); // 來看一下 Dog 的函式內容 (如下右圖)
```
(第 10 行) Bibi 就是透過「Dog 建構函式」產生的,Dog 就是 Bibi 的原型 (狗的藍圖)
(第 12 行) 目前在此原型上並沒有看到狗的原型方法 (如下右圖)

### 透過 prototype 的方式來新增原型方法
只要把 bark 這個 function 指定在 Dog.prototype 上面,所有 Dog 的 instance 都可以共享這個方法。
(承上程式碼) 為狗新增吼叫的能力
* 建構式(constructor)函式本身就是一個物件,<span class="red">**建構式函式物件裡有一個特有屬性 prototype**</span>
* <span class="red">**透過 prototype 所新增的屬性就會作為原型的方法**</span>
* Bibi 物件的 prototype 是 Dog.prototype;換句話說,Bibi 繼承自 Dog.prototype
```typescript=13
// (Dog.prototype 即為 Dog 的原型)
Dog.prototype.bark = function (){ // 在 Dog 函式上使用 prototype 來新增原型的方法
console.log(this.name + ' 吠叫'); // 使用 this 來綁定原本函式的名稱
}
console.log(Bibi, Pupu); // (結果如下圖)
Bibi.bark(); // 比比 吠叫 (有吼叫的能力)
Pupu.bark(); // 噗噗 吠叫 (有吼叫的能力)
console.log(Dog.prototype === Bibi.__proto__) // true
```

#### 再複習一下 JavaScript 的基礎 :
你有一個叫做 Dog 的函數,就可以把 Dog 當作建構函式 (constructor),使用 new 來建立出一個 Dog 的實體 (instance),並且可以在 Dog.prototype 上面加上你想讓所有實體 (instance) 共享的屬性或是方法。
### Prototype 原型
1. 共用的屬性或方法,不用每次都幫實體建立一份,提出來放到 prototype 即可。
2. 將 bark 這個共用的方法放到 Dog.prototype,暫且稱它為 Dog 的原型。
3. JavaScript 的物件能夠「繼承」其 prototype 的屬性或方法。
### 總結
1. 先建立建構函式 (普通的 function) → 透過這個 function 來建立物件
2. 透過 new 運算子產生新的物件實體 (instance)
3. 新產生的物件會把狗函式做為原型使用,也會把 this 套用在建構函式上
4. 建構函式 (狗原型) 是共用的,有共用的屬性和方法
5. 當新物件使用的方法很多時,會消耗很多記憶體
6. 原型優勢 : 透過原型的方式,只要一個記憶體就可以產生大量的物件
### 重點
1. <span class="red">**prototype 是建構函式(constructor)**</span> 特有的原型屬性
2. _ proto _ **物件上**連結原型的屬性,並非正式的屬性
3. <span class="red">**若要從原型新增方法,最好從建構函式裡的 prototype 原型作調整;**</span> 若從任意新增物件調整原型的話,維護上有很大的問題 (主要原因是會讓原型難以被追朔)
### 注意 : 請勿修改原生原型
以下範例來說:
由 Dog 所產生的 Bibi,卻可以改到 Dog 的原型,往後如果發現錯誤,卻無法從 "Dog" 這個建構函式找到。如果在大型專案或是有拆分多個檔案時,這個問題將更難以被發現。
```
function Dog(name, color, size) {
this.name = name;
this.color = color;
this.size = size;
}
Dog.prototype.bark = function () {
console.log(this.name + '吼叫');
};
// 用建構函式的產生的片段,並透過 __proto__ 覆蓋了原型的內容
var Bibi = new Dog('比比', '棕色', '小');
Bibi.__proto__.bark = function() {
console.log(this.name + '亂叫')
}
// 受影響的物件,無法使用 Dog 追朔到源頭
var Puppy = new Dog('帕比', '棕色', '小');
Puppy.bark(); // 帕比 亂叫
```
### 注意 : 不推薦直接去修改不屬於你的 Object
有些人會直接在 Array.prototype 上面加一些函式,讓自己可以更方便地做一些操作,原理也是這樣。可是一般來說,不推薦直接去修改不屬於你的 Object。
```
Array.prototype.last = function () {
return this[this.length - 1];
};
console.log([1,2,3].last()) // 3
```
### 補充
* console.dir() 可以顯示一個對象的所有屬性和方法(詳細打印,利於分析對象)
* console.log() 會在瀏覽器控制臺打印信息
## :memo: 學習回顧
:::info
* 使用建構式的方式產生物件,並且會繼承同一個原型。
* 建構式(constructor)函式本身就是一個物件,他有一個特有屬性 prototype
* 透過 prototype 所新增的屬性就會作為原型的方法
### new 運算子
* 建立一個新的物件,並且連結回原本的建構物件 (指 DOG 函式)
* 將物件的 _ proto _ 指向建構子的 prototype,形成原型串鏈
* <span class="red">**在建構函式內使用的 this 就會綁定在新物件上**</span>
* 將建構子的 this 指向 new 出來的新物件
* 回傳這個物件
* 透過 「new 運算子」搭配「Dog 建構函式」來產生新的物件「狗的實體」,新的物件和原本物件沒有關聯性
### 重點
* 若要從原型新增方法,最好從建構函式裡的 prototype 原型作調整; 若從任意新增物件調整原型的話,維護上有很大的問題 (主要原因是會讓原型難以被追朔)
* 建構函式(函式實體) - prototype - function 原型 - object 原型
* 物件實體 - _ proto _ (等同 prototype)- object 原型
:::
## :+1: 相關參考文件
:::info
[桑莫。夏天。 - 原型](https://cythilya.github.io/2018/10/26/prototype/)
[JavaScript Prototype (原型) 是什麼?](https://shubo.io/javascript-prototype/)
:::
<style>
.red {
color: red;
}
.green {
color: green;
}
</style>