iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0
Modern Web

Hello TypeScript 菜鳥系列 第 16

Day 15. TypeScript Class 類別:Member visibility、readonly、abstract

  • 分享至 

  • xImage
  •  

今天要提到的是TypeScript才有的語法,而TypeScript的這些語法擴展了JavaScript缺乏的一些OOP(Objected-oriented Programming)性質


Member Visibility: public, protected, private

在JavaScript裡,目前只有 publicprivate 用法,並且預設class的屬性和方法都是 public,不過JavaScript執行環境必須支持ES10(ES2019)語法才能使用 private (#) 語法。

TypeScript在類似語法方面則是新增三個關鍵字 publicprotectedprivate

  1. public 代表類別定義的屬性或方法能在該類別以外的環境存取;
  2. private 表示類別定義的屬性或方法只能在被定義的類別內部存取,通常都會透過一個 public 函式來存取 private 屬性;
  3. protected 只能在被定義的類別和繼承(inherit)該類別的子類別(sub-class)所存取。
class Person {
	private id: number;
	protected first_name: string;
	protected last_name: string;
	protected birth_year: number;
	
	constructor(id: number, first_name: string, last_name: string, birth_year: number) {
		this.id = id;
		this.first_name = first_name;
		this.last_name = last_name;
		this.birth_year = birth_year;
	}
	
	public getName(): string {
		return `${this.first_name} ${this.last_name}`;
	});
	
	
	public getBirthYear(): number {
		return this.birth_year;
	}
	
	
	public introduce(): string {
		return `My name is ${first_name}.`;
	}
}


let Bob = new Person(1234567, 'Bob', 'Jonason', 1989);

console.log(Bob.getName());	// Bob Jonason
console.log(Bob.birth_year);	// Compile error
console.log(Bob.getBirthYear());	// 1989

如果在class外部存取 private 屬性或方法,TypeScript能在編譯期就發現錯誤,例如上面嘗試在Person 外部存取Bob的 birth_year 屬性就出現編譯錯誤。

此外,TypeScript也有另一種比較簡潔的寫法 ─ 就是把宣告屬性和存取限制寫在constructor裡:

class Person {

	constructor(private id: number, protected first_name: string, protected last_name: string, protected birth_year: number) {
		this.id = id;
		this.first_name = first_name;
		this.last_name = last_name;
		this.birth_year = birth_year;
	}
	
	public getName(): string {
		return `${this.first_name} ${this.last_name}`;
	});
	
	
	public getBirthYear(): number {
		return this.birth_year
	}
	
	public introduce(): string {
		return `My name is ${first_name}.`
	}
}

和JavaScript一樣,TypeScript類別的預設屬性或方法都是 public,所以範例中的 public 關鍵字也可以省略。


Readonly

readonly 是用來限制屬性(property)不能在constructor以外的地方被賦值,也不能被重新賦值。readonly 的優點是可以在class外部去作存取,但無法修改它。

假設這次在class外部新增一個計算年齡 calcAge 的函式,而 calcAge 不一定是只能給 Person class使用,也能給有繼承 Person birth_year 屬性的子類別和其他有 birth_year 屬性的class用,所以並沒有把 calcAge 放在 Person 裡。

因此這個範例希望可以存取 Person 物件的 birth_year 屬性:

class Person {
	private id: number;
	protected first_name: string;
	protected last_name: string;
	readonly birth_year: number;	// 注意這行其實是public屬性
	protected hasChildren: boolean;
	
	constructor(id: number, first_name: string, last_name: string, age: number) {
		this.id = id;
		this.first_name = first_name;
		this.last_name = last_name;
		this.age = age;
	}
	
	resetBirthYear(): {	// This is an error method
		this.birth_year = 1900;	// error
	}
	
	getName(): string {
		return ${this.first_name} ${this.last_name}`;
	};
	
	
	getBirthYear(): number {
		return this.birth_year;
	}
}


const Bob = new Perso(1234567, 'Bob', 'Jonason', 35, true);


function calcAge(curr, birth_year){
	return curr-birth_year;
}

console.log(calcAge(2022, Bob.birth_year));	// 33

這時候就能善加利用 readonly 關鍵字讓 birth_year 能被外部函式存取,又能避免修改到屬性。

雖然 readonly 用法聽起來跟 const 的作用有點像,但是 const 是用來修飾變數(variable)的關鍵字。


Abstract class 抽象類別

最後要介紹一種蠻特別的類別類型 ─ abstract class。

abstract class本身無法用 new 關鍵字去建立一個屬於該類別的物件,abstract class會定義至少一個抽象方法(abstract method),讓其他類別去繼承並且實作(implement)。

抽象方法是指abstract class會寫出一些子類別必須擁有的方法,但這些方法只有方法名稱,並不會具體地在方法內部寫出該方法會做些什麼,而是讓其他類別繼承方法所屬的抽象類別之後,再詳細地實踐方法的行為。

光聽概念可能會對抽象類別的定義有點模糊,接下來就把前面的 Person class 改寫成abstract class,而要創造abstract class需要用 abstract 關鍵字:

abstract class Person {
	
	constructor(private first_name: string, private last_name: string){}
	
	abstract getBirthYear();
	abstract getAbility();
	
	get introduce():{
		return `${this.first_name} ${this.last_name} can {this.getAbility()}.`
	}
}

class SuperHero extends Person {

	constructor(first_name: string, last_name: string, private birth_year: number, private ability: string){
		super(first_name, last_name);
		this.birth_year = age;
		this.ability = ability;
	}

	getBirthYear(): number {
		return this.birth_year;
	}
	
	getAbility(): string {
		return this.ability;
	}
	
}

const Bob = new Person('Bob', 'Jonason');	// error

const superMan = new SuperHero('Clark', 'Kent', 1938, 'fly');

superMan.introduce();	// Clark Kent can fly.

TypeScript有另一種類似於abstract class的寫法,是讓class用 implements 關鍵字去實作interface的方法,但因為還沒正式談到interface,所以這邊些略過不提。

不過明天就會提到interface了,好耶~


參考資料
TypeScript Official Docs
TypeScript Tutorial
W3Schools Online Web Tutorials
Classes in JS: Public, Private and Protected
Public class fields (JavaScript) @MDN
Private class features (JavaScript) @MDN
Difference between const and readonly in typescript


上一篇
Day 14. TypeScript Class 類別:基本語法
下一篇
Day 16. TypeScript interface 介面:基本語法
系列文
Hello TypeScript 菜鳥31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言