---
# System prepended metadata

title: JavaScript 繼承機制
tags: [Lidemy-MTR05, Week16]

---

# JavaScript 繼承機制

## Class

JavaScript 沒有像其他物件導向程式設計的語言一樣，有內建 class 的機制，就算是 ES6 的 class 也只是語法糖，不過我們還是可以設計出一個類似 class 機制的功能，而可以達成這個目標靠的就是 [原型鏈（Prototype Chain）](https://hackmd.io/@allenliao/SkmnwKd3O)。

### 讓你媽媽 new 一下

在 Java，我們要在一個 class 建一個新的 Instance 時，可以這樣寫：

```java
Person p1 = new Person();
```

這行程式碼會建立一個 `Person` 的 instance 叫做 `p1`，而 JavaScript 也有 `new` 這個關鍵字，而 JavaScript 並沒有 class，所以後面接的會是一個 constructor function（建構函式），就像是 Java 在 `new` 一個新的 instance 時，都會呼叫 class 的 constructor（建構子）。

舉例來說，有個叫做 `Person` 的建構函式，`p1` 是一個 `Person` 的 instance。

```javascript
function Person(name) {
  this.name = name
}

var p1 = new Person('allen')
console.log(p1) // Person, { name: 'allen' }
```

這時建構函式裡面的 `this` 就是這個新的 instance `p1`。

我們也可以在 `Person` 裡面加入一些方法，並再建立一個新的 instance，也會同時有 `name` 跟 `sayHello` 可以用。

```javascript
function Person(name) {
  this.name = name
  this.sayHello = function() {
    console.log(this.name + ': hello~')
  }
}

var p1 = new Person('allen')
var p2 = new Person('kaneshiro')
p1.sayHello() // allen: hello~
p2.sayHello() // kaneshiro: hello~
```

### new 的小問題

我們現在的設計，沒有辦法共享這些屬性跟方法，這是什麼意思？`p1` 跟 `p2` 不是都有 `name` 跟 `sayHello` 嗎？

我們可以做一個測試就知道為什麼會這樣說了。

```javascript
function Person(name) {
  this.name = name
  this.sayHello = function() {
    console.log(this.name + ': hello~')
  }
}

var p1 = new Person('allen')
var p2 = new Person('kaneshiro')
console.log(p1.sayHello === p2.sayHello) // false
```

也就是說，`p1` 跟 `p2` 的 `sayHello` 是兩個不一樣的 function，頂多只是做一樣的事情而已，他們會各自占用一部分記憶體，如果有一百個甚至更多個 instance，他們各自的屬性跟方法就會各自占用一部份的空間，即使他們完全相同，造成很大的浪費。

### Prototype

這時就用到了一個叫做 `prototype` 的屬性，只要把我們想要共享的屬性或方法，放到 `prototype`，所有 `Person` 的 instance 就可以共用這些屬性和方法，而其他不需要共享的就放在建構函式裡。

```javascript
function Person(name) {
  this.name = name
}

Person.prototype.sayHello = function() {
  console.log(this.name + ': hello~')
}

var p1 = new Person('allen')
var p2 = new Person('kaneshiro')
p1.sayHello() // allen: hello~
p2.sayHello() // kaneshiro: hello~
console.log(p1.sayHello === p2.sayHello) // true
```

我們可以看到 `p1` 跟 `p2` 用的的確是同一個 `sayHello`，而且功能完全一樣。

## 總結

我們宣告了一個叫做 `Person` 的函式，這個函式可以稱為建構函式（constructor function），並把它當作 constructor 使用，我們還可以在 `Person.prototype` 加上你想要讓 instances 共享的屬性和方法。

再來用 `var p1 = new Person('allen')` 來建立一個新的 instance，而 `p1` 就可以有自己的 `name`，還有跟其他 instances 共用的 `sayHello`，這樣看起來就很像是 `p1` **繼承**了 `Person` 的 `prototype`。

## 參考資料

[該來理解 JavaScript 的原型鍊了](https://blog.huli.tw/2017/08/27/the-javascripts-prototype-chain/)
[Javascript继承机制的设计思想](https://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html)
[new operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new)

###### tags: `Lidemy-MTR05`, `Week16`