# 物件導向程式設計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 語言的範例 : ![](https://i.imgur.com/kVZvHT9.png) - **複寫(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)