# 物件導向程式設計Object-oriented Programming (OOP)
###### tags: `物件導向` `TypeScript`
> [name=Potter]
> [time=Wed, Sep 14, 2022 10:42 AM]
> 學習並理解中,有錯誤麻煩幫忙指出 :+1:
---
:::spoiler 本篇目錄 :+1::+1::+1:
[TOC]
:::
## 物件導向 Object-oriented(OO)
物件導向暱稱為oo,是電腦科學領域中的一種程式設計方式。
透過**類別 (class)** 的**函式建構式 (function constructor)** 可以產生**物件實例 (object instance)** ,而這個類別產生物件實例的過程稱作**實例化 (instantiation)**。
JavaScript 中使用一種名為 **建構式函式 (constructor functions)** 的函式來定義物件和他們的特色,ES6 之後推出 **class 語法糖** 來建立建構式函式。
### 基本概念 :
> 5、6、7 點為 TypeScript 幫忙補上因為 JavaScript 是弱型別語言而有的問題。
### 1. 類別(Class)
定義一件事物的抽象特點,包含他的屬性和方法。
類別相當於設計藍圖,其中我們可以定義這張藍圖抽象的內容 (屬性、方法)。
可能包括:
- 屬性 : 需要記憶的資訊。
- 方法 : 能夠提供的服務。
- 建構子 (constructor) : 程式執行時第一個去執行的方法,常用於設定程式初始狀態。使用 new 產生實例的時候,會自動呼叫。
```javascript=
class Person {
constructor(name, interests) {
this.name = name;
this.interests = interests;
}
greeting() {
console.log(`Hi I'm ${this.name}`);
}
}
```
### 2. 物件(Object)
類別的實例 ( 透過==new==產生,會自動呼叫建構函式 ),可以由同一個類別去創建許多不同的實例,且這些實例的資料彼此之間互相不影響,都是獨立的。
```javascript=
const potter = new Person('Potter', 'computer science');
const andy = new Person('Andy', 'sleep');
// potter 物件跟 andy 物件有各自的名字(資料),由同類別所建立的獨立物件且互不影響。
```
### 3. 物件導向的三大特性:
**3.1 封裝(Encapsulation)**
一個抽象(abstract)的概念,物件內部資料隱藏,只透過物件本身提供的介面 (interface) 取得物件內部屬性或方法,只需要理解外在,不需要理解內部的構造。
**3.2 繼承(Inhertitance)**
繼承者可以繼承被繼承者的屬性和方法,也能新增自己特有的屬性和方法。
**3.3 多型(Polymorphism)**
簡單來說就是相同名稱的方法(Method),**多個相同名稱的方法,傳入不同的參數,會執行不同的敘述** 包含多載 (Overloading) 和複寫 (Overriding)
父類別可透過子類別衍伸成多種形態,而父類別為子類別的通用型態。
- **多載(Overloading)**
只要==方法的參數型態或數目不同,則**允許多個相同名稱**的方法存在==。但要注意多載並不包含回傳型態不同,也就是如果方法的名稱相同,參數型態及數目相同,而只有回傳型態不同,仍有命名衝突的錯誤。
此為 Java 語言的範例 :

- **複寫(Overriding)**
子類別可以複寫父類別的方法內容,使其擁有不同於父類別的行為。
簡單來說就是改寫她的方法 (Method)。
### 4. 存取器 (getter & setter)
用以改變屬性的讀取和賦值行為。
### 5. 修飾符 (Modifiers)
修飾符是一些關鍵字,用於限定成員或型別的性質。比如==public==表示公有屬性或方法。
### 6. 抽象類別 (Abstract Class)
抽象類別是供其他類別繼承的基底類別,抽象類別不允許被實例化。
抽象類別中的抽象方法必須在子類別中被實現。
可以當作被拿來繼承使用的類別。
### 7. 介面 (Interface)
不同類別之間公有的屬性或方法,可以抽象成一個介面。介面可以被類別實現(implements)。一個類別只能繼承自另一個類別,但是可以實現多個介面。
---
# ES6 中類別的用法 :+1:
## 屬性和方法
使用==class== 定義類別,使用==constructor== 定義建構函式。
透過==new== 產生新實例的時候,會自動呼叫建構函式。
```javascript=
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`;
}
}
let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
```
## 類別的繼承
使用==extends== 關鍵字實現繼承,子類別中使用==super== 關鍵字來呼叫父類別的建構函式和方法。
```javascript=
class Cat extends Animal {
constructor(name) {
super(name); // 呼叫父類別的 constructor(name)
console.log(this.name);
}
sayHi() {
return 'Meow, ' + super.sayHi(); // 呼叫父類別的 sayHi()
}
}
let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
```
## 存取器
使用 getter 和 setter 可以改變屬性的賦值和讀取行為:
```javascript=
class Animal {
constructor(name) {
this.name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
```
## 靜態方法
使用==static== 修飾符修飾的方法稱為靜態方法,他們不需要實例化,而是直接透過==類別==來呼叫:
```javascript=
class Animal {
// 不會被實例化,所以物件實例不會有這個方法,只有類別有這個靜態方法能使用。
static isAnimal(a) {
return a instanceof Animal;
}
}
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
```
# ES7 中類別的用法 :+1:
## 實例屬性
ES6 中實例的屬性只能透過建構函式中的 ==this.xxx== 來定義,ES7 提案中可以直接在類別裡面定義:
```javascript=
class Animal {
name = 'Jack';
constructor() {
// ...
}
}
let a = new Animal();
console.log(a.name); // Jack
```
## 靜態屬性
ES7 提案中,可以使用 ==static== 定義一個靜態屬性:
```javascript=
class Animal {
static num = 42;
constructory() {
// ...
}
}
console.log(Animal.num); // 42
```
# TypeScript 中類別的用法 :+1:
## public、private 和 protected
TypeScript 可以使用三種訪問修飾符 (Access Modifiers) ,分別是 ==public==、==private== 和 ==protected==。
- **public** 修飾的屬性或方法是公有的,可以在任何地方被訪問到,預設所有的屬性和方法都是 public 的。
- **private** 修飾的屬性或方法是私有的,不能再宣告他的類別的外部訪問。
- **protected** 修飾的屬性或方法是受保護的,他和 ==private== 類似,區別是他在子類別中是允許被訪問的(允許被繼承)。
> 需要注意的是,TypeScript 編譯之後的程式碼中,並沒有限制 private 屬性在外部的可及性。只是在 TypeScript 撰寫當下時會噴錯提醒你,TypeScript 只是模仿強型別語言,在當下會噴錯,但在編譯完後他==本質仍然是 JavaScript==。
## readonly
只讀屬性關鍵字,只允許出現在屬性宣告或索引簽名中。
如果和其他修飾符同時存在的話,需要寫在其後面。
```javascript=
class Animal {
// public readonly name;
readonly name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.
```
## 抽象類別
abstract 用於定義抽象類別和其中的抽象方法。
抽象類別是不允許被實例化的,抽象類別中的抽象方法==必須被子類別實現==。
```javascript=
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
let a = new Animal('Jack');
// index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.
```
下面是一個正確使用抽象類別的例子:
```javascript=
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
class Cat extends Animal {
public sayHi() {
console.log(`Meow, My name is ${this.name}`);
}
}
let cat = new Cat('Tom');
cat.sayHi(); // Meow, My name is Tom
```
> 需要注意的是,即使是抽象方法,TypeScript 的編譯結果中,仍然會存在這個類別。
## 類別的型別
給類別加上 TypeScript 的型別很簡單,與介面類似:
```javascript=
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
```
---
參考資料來源 :
- https://willh.gitbook.io/typescript-tutorial/advanced/class
- https://pjchender.dev/javascript/js-oo/
- https://www.happycoding.today/posts/49
- [關於 Object ,一口氣全說完
](https://medium.com/enjoy-life-enjoy-coding/javascript-%E9%97%9C%E6%96%BC-object-%E4%B8%80%E5%8F%A3%E6%B0%A3%E5%85%A8%E8%AA%AA%E5%AE%8C-4bb924bcc79f)