TypeScript-(四)類別形式的物件導向

在 TypeScript 中採用了「類別形式的物件導向」,需要先定義「類別(Class)」,再根據類別產生實體(Instance),建立出實體的物件才能夠被操作使用。

1
2
3
4
5
6
7
//定義類別
class 類別名稱{
...類別內容...
}

//產生實體
var 變數名稱:類別名稱 = new 類別名稱(參數);

類別的內容

在定義類別的時候,就可以先規劃其包含的內容,主要分為三大項:

  1. 建構子(Constructor):用來做初始化的動作的特出方法,在建立實體物件時,會執行這個方法
  2. 屬性(Attribute):物件內用來儲存資料值的變數
  3. 方法(Method):物件中的函式功能

以下範例:

  • 首先定義一個名為 Person 的類別
  • Person 類別中有兩個屬性:name、age
  • Person 類別中有一個建構子(Constructor),當這個類別被 new 出來的時候,會傳入兩個參數:n、a,並執行建構子方法中的初始化的動作
  • Person 類別中有一個方法:say
  • 使用 「new」 關鍵字建立出實體,並且指派給 member 這個變數
  • member 可以被稱作實體物件,就可以去操作其中的屬性、方法。例如用 member.print() 呼叫 print 方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Person{
    //屬性
    name:string;
    age:numebr;

    //建構子
    constructor(n:string,a:number){
    this.name = n;
    this.age = a;
    }

    //方法
    say():string{
    let msg:string = "Hi! I'm " + this.name;
    return msg;
    }
    }

    //new 出實體
    let member:Person = new Person("Amy",20);
    console.log(member.print());//印出 Hi! I'm Amy

可以看到在建構子中或是方法中要拿取類別中的屬性,會使用「this.xxx」的格式來取用,例如:this.namethis.age

存取修飾詞(Access Modifier)

類別中的屬性或方法,基本上都可以從外部進行存取,而產生實體物件之後,也可以對於它的屬性跟方法進行改寫。

然而,如果沒有去限制存取權限,就可能會從不恰當的地方進行存取,導致錯誤而無法正常執行程式碼。

因此,TypeScript 中使用「存取修飾詞」,用來「限制屬性或方法可以接受從哪些範圍進行存、取的動作」。

簡單來說,public 修飾詞級即代表可以從外部取用該資源;而 private 只能接受內部的存取。

存取修飾詞的使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
public name:string;
private age:number;

constructor(n:string,y:number){
// ...略
}

print():string{
// ...略
}
}

存取子(Accessor)

當類別中的屬性為敏感資料時,不適合隨意從外部直接存取,因此會將該屬性設定為 private,當我們需要去存取這個屬性時,就需要使用「存取子」的功能,透過它去控制屬性的存取動作。

存取子基本上是以方法的形式來進行定義,不過他跟一般的方法又有些不同M在開頭需要冠上「get」跟「set」關鍵字。

get:純粹用來「取得」屬性值,因此不能傳入參數
set:存粹用來「設定」屬性值,因此不能有回傳值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person{
public name:string;
private age:number;

get ageGetter():number{
return this.age;
}

set ageSetter(year:number){
this.age = year<0 ? 0 : year;
}

constructor(n:string,year:number){
this.name = n;
this.age = year;
}

print():string{
// ...略
}
}

類別的繼承(extends)

繼承正如其名稱,讓新建立的類別可以直接去繼承既有類別中的所有屬性跟方法,透過「繼承」就可以不用重複賺些多個類似的類別功能。

1
2
3
class 新類別名稱 extends 既有類別名稱{
// ...略
}

透過這種「繼承」機制所創建出來的新類別會被稱作既有類別的「子類別(Sub-class)」,而反過來說,作為繼承來源的既有類別則會稱作新類別的「超類別(Super-class)或稱作父類別」。

在子類別中要呼叫執行父類別的建構子,可以使用 super 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person{
public name:string;
private age:number;

constructor(n:string,year:numebr){
this.name = n;
this.age = year;
}

print():string{
let msg:string = "My name is "+this.name;
return msg;
}
}

class Student extends Person{
public grade:number;

constructor(n:string,year:number,g:number){
super(n,year);
this.grade = g;
}

printAll():string{
let msg:string = "My name is "+this.name+". And, I'm in "+this.grade+" grade.";
return msg;
}
}

方法覆寫(Override)

假設子類別中想要使用跟父類別中同名的方法,這稱作「覆寫(override)」,當子類別的實體呼叫該方法時,程式會優先執行子類別中的方法,而不會去執行父類別中的同名方法,因為父類別中的方法已經被子類別的方法所取代。

類別屬性與類別方法

上述內容中,都強調在類別中的屬性跟方法,都必須要「先宣告出實體物件,才能透過實體物件去呼叫」。不過,其實也可以寫成「非隸屬於實體物件的資源」。

想要指定這些資源並非放在個別的實體,而是隸屬於類別身,就需要用「static」修飾詞。如果在屬性或方法宣告時加上 static,那麼該屬性或方法就會被配置在類別的層級,這稱作為「類別屬性」跟「類別方法」,他們無法從個別實體中被呼叫,需要直接從類別別呼叫,也就是使用「類別名稱.屬性名稱」這樣的語法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person{
public name:string;
public age:numebr;
public static checked = false;

constructor(n:string,y:number){
this.name = m;
this.age = y;
}
}

function doClick():void{
Person.checked = true;
}

介面(Interface)

有時候在分工開發時,首位開發者創造了多個類別,而後續接手的開發者無法確定各個類別中的必要屬性跟方法為何,因此可以透過 「介面」來提醒其他開發者,該個類別中必定具備哪些屬性跟方法。

介面可以預先彙整類別中所需要的屬性跟方法,但是不一定要先撰寫完畢其中的內容。

定義介面的語法:

1
2
3
4
interface 介面名稱{
// ...屬性...;
//...方法...;
}

使用介面的語法:

1
2
3
class 類別名 implements 介面{
// ...略
}

這樣參考某個介面的類別,就必須以覆寫的方式去宣告這些介面中的屬性跟方法,如果漏掉了某些項目,就無法被成功編譯。

簡而言之,介面的用意在於「強制要求類別中必須要實作介面中的屬性跟方法」。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
interface Man{
name:string;
age:number;

print():string;
}

class Person inplements Man{
public name :string;
public age :number;

constructor(n:string,y:number){
this.name = n;
this.age = y;
}

print():string{
let msg = "My name is "+ this.name;
return msg;
}
}

class Student implements man{
public name :string;
public age :number;
public grade:number;

constructor(n:string,y:number,g:numebr){
this.name = n;
this.age = y;
this.grade = g;
}

print():string{
let msg:string = "My name is "+this.name+". And, I'm in "+this.grade+" grade.";
return msg;
}
}

var list:Man[] = [];
list.push(new Person("Amy",30));
list.push(new Student("Peter",20,2));

上述程式碼中,使用「list:Man[]」的方式指定此陣列中的型別需要是 Man 型別,而 Person 跟 Student 都是透過 Man 介面所時做出來的類別,因此在 list 陣列中放入 Person 跟 Student 時做出來的實體物件是被接受的。

© 2020 Leah's Blog All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero