---
# System prepended metadata

title: JS原型鍊(Prototype Chain)
tags: [Javascript]

---

---
tags: Javascript
---

# JS原型鍊(Prototype Chain)

物件擁有自己的屬性，他可以繼承原型，而原型也可以繼承其他原型，原先的物件可以使用`.`取用屬性，而如果在原本物件找不到此屬性，他就會向上查找直到找到屬性或是`null`(找到null就會報錯)，而如果有從一個原型新增出兩個實體，他們會共用同一個原型的方法

![](https://i.imgur.com/QzWbujR.jpg)


## 特性

1. 一樣具有物件特性
1. 向上查找
1. 原型可共用發方法及屬性


## 原型在哪裡

**以下寫法為非正規寫法**

以下範例可以看到陣列的原型，也可以為他的原型加上屬性，因為屬性是共用的，假如你新增其他陣列也可以使用`getLast()`方法。

![](https://i.imgur.com/rRZjGfh.jpg)

## 建構式自定義原型

利用函式建構物件，使用`Prototype`製作原型功能，在過去無規範如何製作原型功能，所以大多採用`__proto__`製作，後來`ECMAScript` 規範要使用`[[Prototype]]`製作原型功能，
但之前開發者大多使用`__proto__`原型所以瀏覽器還保留此方法，所以如果現在看到`[[Prototype]]`就是瀏覽器還沒更新前的`__proto__`是往上追朔的原型。

**`proto` 不屬於正規 `JS`，他是瀏覽器的方法，會導致同層所有資料被賦予新方法**

**`prototype` 是正規的`JS`方法，在為建構函式新增方法時、原型作多層串接時都會使用**

以下範例使用函式建構物件

```javascript=
// 利用含有 name 與 city 屬性的 human 函式，建立一個 Joe 物件：
function human(name, city) {
    this.name = name;
    this.city = city;
}

var Joe = new human('joe', 'Tiawan');

// 接著針對 human 函式的原型添加屬性
human.prototype.work = function() {
    console.log(`${this.name}工作`);
}

console.log(Joe.work()); // joe工作

// __proto__ 物件上新增(維護難)
// prototype 函式上新增(建議在原型屬性上新增)
console.log(human.prototype === Joe.__proto__); // true
```

## Object.create 建立多層繼承

使用`Object.create`繼承方法，
當繼承函式執行時，`this` 值指向繼承的物件，而不是在函式內擁有屬性的原型物件。

[為啥總要修正constructor。](https://segmentfault.com/a/1190000016147953)補充

```javascript=
function Animal(family) {
  this.kingdom = '動物界';
  this.family = family; 
}

Animal.prototype.move = function(){
  console.log(`${this.name}走路`);
}

function Cat(name, color, size) {
  Animal.call(this, '貓科'); // 只繼承原型，所以需補上建構函式
  this.name = name;
  this.color = color;
  this.size = size;
}

Cat.prototype = Object.create(Animal.prototype); // 將Animal.prototype當作原型使用
Cat.prototype.constructor = Cat; // 將Cat建構函式補回去，否的話constructor會是Animal建構函式
Cat.prototype.detail = function(){
  console.log(`名子:${this.name}, 顏色:${this.color}, 大小: ${this.size}`);
}

var Amy = new Cat('艾米', '棕色', '小型');
var Kumo = new Cat('哭某', '白色', '大型');
Amy.detail(); // 名子:艾米, 顏色:棕色, 大小: 小型
Amy.move(); // 艾米走路

Kumo.detail(); // 名子:哭某, 顏色:白色, 大小: 大型
Kumo.move(); // 哭某走路
```

## 使用ES6新語法 class建立物件

將上述的繼承與建立物件改成使用class語法，類別的主體指的是被大括號({})包含的部分，你可以在這裡面定義類別成員(members)，例如方法(methods)或建構子(constructors)。

**若在子類別中有建構子(constructor)，要使用this前則必須先呼叫super()函式。**

[class MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Classes)

```javascript=
class Animal{
  constructor(family) {
      this.kingdom = '動物界';
      this.family = family; 
  }
  
  echoFamily() {
    console.log(`${this.name}走路`);
  }
}

class Cat extends Animal {
    constructor(name, color, size) {
      super('貓科');
      this.name = name;
      this.color = color;
      this.size = size;
  }
  
  detail(){
    console.log(`名子:${this.name}, 顏色:${this.color}, 大小: ${this.size}`);
  }
}

var Amy = new Cat('艾米', '棕色', '小型');

Amy.echoFamily();
Amy.detail();
```

[MDN 繼承與原型鏈](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)