TypeScript建立類別

物件導向的概念是一種寫程式碼的風格,是現在比較主流的一種方式,讓程式碼看起來比較有架構簡潔,至於整體的概念網路上有超級多,就不在多寫,主要著重在TypeScript的語法紀錄

如何寫class

首先先練習寫class幾個特點

  1. 習慣第一個字大寫
  2. 可以有屬性 > 變數
  3. 可以有方法 > 函式
  4. 可以使用new建立出來
  5. 屬性跟方法被建立後可以被 “.” 出來用跟修改
  6. constructor是建立之前跑的地方

建立一個簡單的class動物,在實現在貓跟狗上面,所以狗貓就有了name跟age的屬性,還有call跟action這兩個方法,嘗試改變狗的名子最後將兩邊印出來看

class Animal {
  name: string;
  age: number;

  call() {}
  action() {}
}

let dog = new Animal();
let cat = new Animal();

dog.name = "dog";

console.log(dog);
console.log(cat);

可以發現,狗有被賦予名子上去,而貓的名還沒被賦予值

再以剛剛為例,如果想要再new出來之前就有名子,那可以使用constructor來在建立的時候設定參數跟屬性,constructor是在物件被建立之前就會跑的地方

class Animal {
  constructor(name: string) {
    this.name = name;
  }
  name: string;
  age: number;

  call() {}
  action() {}
}

let dog = new Animal("BigDog");
let cat = new Animal("cat");

console.log(dog);
console.log(cat);

class加入interface

前面有提到interface加入物件的方法,現在同樣的方式可以用來約束class的設定,那有幾個特點是

  1. 屬性一樣是設定型態
  2. 則方法是設定回傳值,參數不管

從範例可以知道,我們一樣要跟interface設定的一樣,把屬性建立好,但方法只要遵照他的回傳值,就可以了,參數不用特別設定一樣,也不會報錯,我們使用interface來約束自己,會盡量都遵照interface上面的設定

interface Animal {
  name: string;
  age: number;

  call: (data: any) => string;
  action: (data: number) => string;
  sayHi: (data: string) => void;
}

class dog implements Animal {
  name: string;
  age: number;

  call(data: any) {
    return "";
  }
  action() {
    return "";
  }
  sayHi() {}

  run() {}
  help() {}
}

如果沒有設定interface的屬性及方法,則會報錯提醒

class類別的繼承

這是class的一大特點,可以先設定一個初始大家都共用的class給大家繼承,讓有共同功能的class可以不用一直重複寫一樣的功能

所以根據範例的執行,我們貓跟狗又額外的繼承了Animal的class,這樣好處就是,我想要run這個方法,就不用在額外寫在兩個class上也能使用

class Animal {
  run() {
    console.log("run...");
  }
}

class Dog extends Animal {}
class Cat extends Animal {}

const d1 = new Dog();
const c1 = new Cat();

d1.run();
c1.run();

覆蓋功能

如果你想要把原本的方法給覆蓋掉也是可以,只要在自己的class裡面寫一樣的方法,就可以達成覆蓋效果

class Animal {
  run() {
    console.log("run...");
  }
}

class Dog extends Animal {
  run() {
    console.log("Dog...run...");
  }
}
class Cat extends Animal {}

const d1 = new Dog();
const c1 = new Cat();

d1.run();
c1.run();

super調用父層的功能

因為方法已經被複寫,想要調用父層的方法就要使用super,那就可以把父層的東西又叫出來用

class Animal {
  run() {
    console.log("father...run...");
  }
}

class Dog extends Animal {
  run() {
    super.run();
    console.log("Dog...run...");
  }
}
class Cat extends Animal {}

const d1 = new Dog();

d1.run();

父層的constructor

如果父層有constructor來建構初始內容,那麼建立(new)繼承的class(BigFish),就要依照constructor的參數給予設定並建立

範例中需要name這個參數,所以BigFish繼承了他,就要建構這個初始內容

class Fish {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  swim() {
    console.log("go..." + this.name);
  }
}

class BigFish extends Fish {
  swim() {
    console.log("fish...go..." + this.name);
  }
}

let bigFish = new BigFish("nimo");

bigFish.swim();

抽象類別

這是一個跟interface很像的功能,差別在於,interface不能原本就帶有一些功能,但抽象類別可以

  1. 抽象類別不能被建構(new)出來,只能被繼承
  2. 抽象類別跟interface的差別在於可以附帶一些功能
  3. 使用抽象方法跟屬性,強迫繼承對象實作這些方法跟屬性

範例來看

abstract class Animal {
  abstract name: string;
  run() {
    console.log("abstract function run...");
  }
  abstract call(): void;
  abstract hello(): string;
}

class Dog extends Animal {
  name: string;
  call() {}
  hello() {
    return "hi";
  }
}

let dog = new Dog();
dog.run();

class的成員類型

這個概念有點像是使用權限,總共分為private、public、protected,分別是私有的、公開的、受保護的,直接做一張表來表示他們的使用範圍

 publicprotectedprivate
自己class內可使用可使用可使用
繼承者可使用可使用不可使用
建構(new)可使用不可使用不可使用

直接來測試當被建構出來之後,能使用多少屬性,結論是只有public可以使用

class Animal {
  private name: string;
  public age: number;
  protected action: string;
}

let dog =new Animal()

那如果被繼承的話,可以使用多少權限,只有protected跟public可以使用

呼叫private的方法

如果真的需要使用到private的內容,可使用function把他呼叫出來,這樣可以繼續保有私有狀態不被任意改變,還可以取用裡面的值

class Animal {
  private name: string = "tony";
  public age: number;
  protected action: string;

  get() {
    console.log(this.name);
  }
}

let dog = new Animal();
dog.get();

class的靜態用法

可以把他當作一個全域的用法,不用透過建構(new)的方式,就可以取用class裡面的方法跟屬性,適合共用及需要全域,但又想要整理起來的一種用法

那從範例中的加油站,油的存量不可以隨意更改,但是透過remaining可以取來看,而refuel可以加油來使用加油站內的庫存油

至於為什麼不new出來改,是因為,new出來之後,存量就不再是共用的,而是自己的,所以要共用的東西要用static

class GasStation {
  private static stock: number = 1000;
  static refuel(oil: number) {
    this.stock -= oil;
  }
  static remaining() {
    console.log(this.stock);
  }
}
function car1(oil: number) {
  GasStation.refuel(oil);
  GasStation.remaining();
}

car1(400);
car1(300);

總結

今天也是繼續學習布魯斯的 TypeScript 入門攻略|輕鬆打造實時聊天室這堂課,說到物件導向這個概念,其實我都一直想把他學好,但不是中途有事情,或是學過概念以後都沒用到,但這次重新學習TypeScript就是要把物件導向的觀念深植在我之後的程式碼當中,雖然我在JavaScript一直都有用物件在整理程式碼,但實際能做到事情還是跟class有很大程度的落差

透過課程的學習,我在這章節清楚的class以前完全搞不清楚的地方,全部都搞懂了,課程中清楚的比對每個方法的作用,讓我一直以來覺得沒必要的功能,現在都知道他原本的用途了,雖然有寫文章做筆記,但這只是讓我快速回憶的筆記,對於真正想學習的人,還是會建議聽過布魯斯的解講,比自己花時間理解還快上許多

By dong

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。