第 7 課:類別 (Classes)
學習目標
- 掌握 TypeScript 中類別的基本語法,包括建構函式、屬性和方法
- 理解並應用類別繼承,包括
super()和方法覆寫 - 學會使用
public,private,protected存取修飾符 - 理解
readonly修飾符在類別屬性中的應用 - 掌握靜態屬性和靜態方法的用法
- 初步認識抽象類別和抽象方法
- 了解類別可以作為介面使用的情況
1. 類別的基本語法
類別 (Class) 是創建物件的藍圖或模板。你可以將它想像成一個模具,用來製造具有相同結構和行為的物件。
基本類別結構
typescript
1class Person {
2 name: string;
3 age: number;
4
5 constructor(name: string, age: number) {
6 this.name = name;
7 this.age = age;
8 }
9
10 greet(): string {
11 return `Hello, I'm ${this.name}`;
12 }
13}🎯 互動練習 1:建立你的第一個類別
💡 參考答案
typescript
1// 完整的 Car 類別
2class Car {
3 brand: string;
4 model: string;
5 year: number;
6
7 constructor(brand: string, model: string, year: number) {
8 this.brand = brand;
9 this.model = model;
10 this.year = year;
11 }
12
13 getInfo(): string {
14 return `${this.year} ${this.brand} ${this.model}`;
15 }
16}
17
18// 測試程式碼
19const myCar = new Car("Toyota", "Camry", 2020);
20console.log(myCar.getInfo());
21console.log(`品牌: ${myCar.brand}`);
22console.log(`型號: ${myCar.model}`);
23console.log(`年份: ${myCar.year}`);2. 存取修飾符
TypeScript 提供了三種存取修飾符來控制類別成員的可見性:
public(預設):可以在任何地方存取private:只能在類別內部存取protected:可以在類別內部和子類別中存取
🎯 互動練習 2:存取修飾符實作
💡 參考答案
typescript
1class BankAccount {
2 public accountNumber: string; // 公開的帳戶號碼
3 private balance: number = 0; // 私有的餘額
4 protected bankName: string; // 受保護的銀行名稱
5
6 constructor(accountNumber: string, bankName: string) {
7 this.accountNumber = accountNumber;
8 this.bankName = bankName;
9 console.log("帳戶建立成功");
10 }
11
12 public deposit(amount: number): void {
13 this.balance += amount;
14 console.log(`存款 $${amount},餘額: $${this.balance}`);
15 }
16
17 public withdraw(amount: number): void {
18 if (this.balance >= amount) {
19 this.balance -= amount;
20 console.log(`提款 $${amount},餘額: $${this.balance}`);
21 } else {
22 console.log(`餘額不足,無法提款 $${amount}`);
23 }
24 }
25
26 public getBalance(): number {
27 return this.balance;
28 }
29}
30
31// 測試程式碼(請勿修改)
32const account = new BankAccount("ACC123", "台灣銀行");
33console.log(`帳戶號碼: ${account.accountNumber}`);
34
35account.deposit(500);
36account.withdraw(200);
37account.withdraw(400);
38
39// 下面這行會報錯,因為 balance 是 private
40// console.log(account.balance);3. 類別繼承
類別可以繼承其他類別的屬性和方法,使用 extends 關鍵字。子類別可以覆寫父類別的方法,也可以添加新的功能。
🎯 互動練習 3:類別繼承實作
💡 參考答案
typescript
1// 基本動物類別
2class Animal {
3 protected name: string;
4
5 constructor(name: string) {
6 this.name = name;
7 }
8
9 makeSound(): void {
10 console.log("動物叫聲: 未知");
11 }
12
13 move(): void {
14 console.log(`${this.name}在移動`);
15 }
16}
17
18// Dog 類別,繼承 Animal
19class Dog extends Animal {
20 constructor(name: string) {
21 super(name); // 調用父類別的建構函式
22 }
23
24 makeSound(): void {
25 console.log("動物叫聲: 汪汪汪");
26 }
27
28 move(): void {
29 console.log("小狗在跑步");
30 }
31}
32
33// Cat 類別,繼承 Animal
34class Cat extends Animal {
35 constructor(name: string) {
36 super(name); // 調用父類別的建構函式
37 }
38
39 makeSound(): void {
40 console.log("動物叫聲: 喵喵喵");
41 }
42
43 move(): void {
44 console.log("小貓在爬樹");
45 }
46}
47
48// 測試程式碼(請勿修改)
49const dog = new Dog("小狗");
50dog.makeSound();
51dog.move();
52
53const cat = new Cat("小貓");
54cat.makeSound();
55cat.move();4. 唯讀屬性和靜態成員
唯讀屬性 (readonly)
使用 readonly 修飾符可以讓屬性在初始化後不能被修改。
靜態成員 (static)
靜態成員屬於類別本身,而不是類別的實例。
🎯 互動練習 4:唯讀屬性和靜態成員
💡 參考答案
typescript
1class Circle {
2 readonly radius: number; // 唯讀屬性
3 static count: number = 0; // 靜態屬性,記錄建立的圓形數量
4 static readonly PI: number = 3.14159; // 靜態唯讀屬性
5
6 constructor(radius: number) {
7 this.radius = radius;
8 Circle.count++; // 增加計數
9 console.log(`圓形建立: 半徑 ${radius}`);
10 }
11
12 getArea(): number {
13 return Circle.PI * this.radius * this.radius;
14 }
15
16 getCircumference(): number {
17 return 2 * Circle.PI * this.radius;
18 }
19
20 static getCount(): number {
21 return Circle.count;
22 }
23
24 // 顯示圓形資訊
25 displayInfo(): void {
26 console.log(`面積: ${this.getArea().toFixed(2)}`);
27 console.log(`周長: ${this.getCircumference().toFixed(2)}`);
28 console.log(`總共建立了 ${Circle.getCount()} 個圓形`);
29 }
30}
31
32// 測試程式碼(請勿修改)
33const circle1 = new Circle(5);
34circle1.displayInfo();
35
36const circle2 = new Circle(3);
37circle2.displayInfo();
38
39// 嘗試修改唯讀屬性會報錯
40// circle1.radius = 10; // 錯誤5. 抽象類別
抽象類別不能被直接實例化,只能被繼承。它們通常包含抽象方法,子類別必須實作這些方法。
🎯 互動練習 5:抽象類別實作
💡 參考答案
typescript
1// 抽象類別 Shape
2abstract class Shape {
3 protected name: string;
4
5 constructor(name: string) {
6 this.name = name;
7 }
8
9 // 具體方法
10 displayName(): void {
11 console.log(`形狀: ${this.name}`);
12 }
13
14 // 抽象方法
15 abstract calculateArea(): number;
16 abstract calculatePerimeter(): number;
17
18 // 使用抽象方法的具體方法
19 displayInfo(): void {
20 this.displayName();
21 console.log(`面積: ${this.calculateArea()}`);
22 console.log(`周長: ${this.calculatePerimeter()}`);
23 }
24}
25
26// Rectangle 類別
27class Rectangle extends Shape {
28 private width: number;
29 private height: number;
30
31 constructor(width: number, height: number) {
32 super("長方形");
33 this.width = width;
34 this.height = height;
35 }
36
37 calculateArea(): number {
38 return this.width * this.height;
39 }
40
41 calculatePerimeter(): number {
42 return 2 * (this.width + this.height);
43 }
44}
45
46// Triangle 類別
47class Triangle extends Shape {
48 private base: number;
49 private height: number;
50 private side1: number;
51 private side2: number;
52
53 constructor(base: number, height: number, side1: number, side2: number) {
54 super("三角形");
55 this.base = base;
56 this.height = height;
57 this.side1 = side1;
58 this.side2 = side2;
59 }
60
61 calculateArea(): number {
62 return (this.base * this.height) / 2;
63 }
64
65 calculatePerimeter(): number {
66 return this.base + this.side1 + this.side2;
67 }
68}
69
70// 測試程式碼(請勿修改)
71const rectangle = new Rectangle(4, 5);
72rectangle.displayInfo();
73
74console.log("---");
75
76const triangle = new Triangle(4, 3, 4, 4);
77triangle.displayInfo();
78
79// 抽象類別不能被實例化
80// const shape = new Shape("形狀"); // 錯誤6. 綜合練習
🎯 互動練習 6:員工管理系統
💡 參考答案
typescript
1// 基本員工類別
2class Employee {
3 protected name: string;
4 protected position: string;
5 protected salary: number;
6
7 constructor(name: string, position: string, salary: number) {
8 this.name = name;
9 this.position = position;
10 this.salary = salary;
11 }
12
13 getBasicInfo(): void {
14 console.log(`姓名: ${this.name}`);
15 console.log(`職位: ${this.position}`);
16 console.log(`薪水: $${this.salary}`);
17 }
18
19 calculateTotalSalary(): number {
20 return this.salary;
21 }
22
23 // 顯示完整資訊
24 displayFullInfo(): void {
25 this.getBasicInfo();
26 console.log(`總薪資: $${this.calculateTotalSalary()}`);
27 }
28}
29
30// 開發者類別,繼承 Employee
31class Developer extends Employee {
32 private bonus: number;
33
34 constructor(name: string, salary: number, bonus: number) {
35 super(name, "軟體工程師", salary);
36 this.bonus = bonus;
37 }
38
39 getBasicInfo(): void {
40 console.log(`姓名: ${this.name}`);
41 console.log(`職位: ${this.position}`);
42 console.log(`薪水: $${this.salary}`);
43 console.log(`獎金: $${this.bonus}`);
44 }
45
46 calculateTotalSalary(): number {
47 return this.salary + this.bonus;
48 }
49}
50
51// 經理類別,繼承 Employee
52class Manager extends Employee {
53 private teamSize: number;
54
55 constructor(name: string, salary: number, teamSize: number) {
56 super(name, "經理", salary);
57 this.teamSize = teamSize;
58 }
59
60 getBasicInfo(): void {
61 console.log(`姓名: ${this.name}`);
62 console.log(`職位: ${this.position}`);
63 console.log(`薪水: $${this.salary}`);
64 console.log(`管理團隊人數: ${this.teamSize}`);
65 }
66
67 private getManagementBonus(): number {
68 return this.teamSize * 100;
69 }
70
71 calculateTotalSalary(): number {
72 return this.salary + this.getManagementBonus();
73 }
74
75 // 額外顯示管理獎金
76 displayFullInfo(): void {
77 this.getBasicInfo();
78 console.log(`管理獎金: $${this.getManagementBonus()}`);
79 console.log(`總薪資: $${this.calculateTotalSalary()}`);
80 }
81}
82
83// 測試程式碼(請勿修改)
84console.log("員工資訊:");
85const developer = new Developer("張三", 5000, 500);
86developer.displayFullInfo();
87
88console.log("---");
89
90console.log("員工資訊:");
91const manager = new Manager("李四", 8000, 10);
92manager.displayFullInfo();7. 知識檢查
🎯 互動練習 7:類別知識測驗
💡 參考答案
typescript
1// 類別知識測驗 - 正確答案
2let answer1: string = "B"; // 類別的建構函式使用 constructor 關鍵字
3let answer2: string = "C"; // private 修飾符只能在類別內部存取
4let answer3: string = "A"; // 類別繼承使用 extends 關鍵字
5let answer4: string = "B"; // 靜態成員屬於類別本身
6let answer5: string = "C"; // 抽象類別不可以被直接實例化
7
8// 顯示答案(請勿修改下面的程式碼)
9console.log("第1題答案:" + answer1);
10console.log("第2題答案:" + answer2);
11console.log("第3題答案:" + answer3);
12console.log("第4題答案:" + answer4);
13console.log("第5題答案:" + answer5);總結
今日學到的重點
- 類別基本語法:使用
class關鍵字定義類別,constructor建構函式初始化物件 - 存取修飾符:
public(公開)、private(私有)、protected(受保護) - 類別繼承:使用
extends繼承父類別,super()調用父類別方法 - 唯讀屬性:使用
readonly讓屬性在初始化後不可修改 - 靜態成員:使用
static定義屬於類別本身的屬性和方法 - 抽象類別:使用
abstract定義不能直接實例化的類別
課後建議
- 練習設計不同的類別結構
- 嘗試使用繼承來組織程式碼
- 理解何時使用不同的存取修飾符
- 重複做上面的互動練習,直到完全理解
下一課預告
下一課我們會學習:
- 泛型的基本概念和語法
- 泛型函式和泛型類別
- 泛型約束和條件類型
- 實用的泛型工具類型
恭喜你完成第七課! 🎉
你已經掌握了 TypeScript 類別的核心概念。類別是物件導向程式設計的基礎,能幫助你組織和重用程式碼!