Deno 入門指南
  • 前言
  • Deno 更新日誌
  • 簡介
    • Deno 跟 Node.js 的主要差異
    • Hello, World!
  • TypeScript 基礎篇
    • 變數宣告
    • 使用型別系統
    • 流程判斷與迴圈
    • 函式宣告
    • This 與 Arrow Function
    • 在函式中應用強型別
    • 介面
    • 型別別名
    • 物件導向概念
    • 類別的封裝與繼承
    • 介面與類別、抽象類別
    • 泛型的概念與實作
    • 型別補充
  • Deno CLI
    • 快速開始
    • 沙盒機制
    • URL Import
    • 編譯選項
    • 相關工具及測試
      • WebGPU API
      • Deno.resolveDns
      • 程式碼編譯器
      • 程式碼檢查器
      • 依賴檢查器
      • 文件產生器
      • 程式碼打包工具
      • 腳本安裝
      • 程式碼格式化
      • Deno 命名空間與編譯器 API
      • 使用 Deno 進行測試
  • 使用 Deno 打造多線程應用
    • 多線程概念
    • Deno Workers
    • 使用多線程計算矩陣相乘
  • 使用 Deno 打造 Web API
    • Web API 介紹
    • Oak 框架介紹
    • 使用 Denon 精簡指令
    • 實作 Web API
    • MongoDB 安裝教學
    • Deno 與 MongoDB 共舞
    • 完成第一支 Web API
    • 淺談跨來源資源共用(CORS)與解決辦法
Powered by GitBook
On this page
  • 屬性和方法
  • 類別的繼承
  • 存取器
  • 靜態方法
  • 公用私用傻傻分不清楚?!
  • Reference
  • 延伸閱讀

Was this helpful?

  1. TypeScript 基礎篇

類別的封裝與繼承

前一篇章節向大家帶來了 OOP 的概念,本篇開始將針對 Class 的實作開始講解並介紹: 繼承、封裝、多型等重要概念與實現。

屬性和方法

  • class

    class 是宣告類別名稱的關鍵字。

  • constructor()

    Js/Ts 利用 constructor() 定義好類別實體化的那一刻要做什麼樣的初始化設定。

    constructor() 也可以有一個很專業的名詞 : 建立者函數。

  • new

    宣告完類別以後還沒有結束! 我們需要使用 new 將物件實體化。

    剛開始學習 JavaScript 時,常常會出現 這什麼鬼 Moment , new 這個關鍵字也不例外。

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()

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

    定義 getter 函式作為取得參數的唯一管道 。

  • setter

    定義 setter 函式作為更改參數的唯一管道 。

class Animal {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
  get Name() {
    return this.name;
  }
  set Name(value) {
    this.name = value;
    console.log("setter: " + this.name);
  }
}
let a = new Animal("Kitty");
console.log(a.Name); // Kitty
a.Name = "Tom"; // setter: Tom
console.log(a.Name); // Tom

為了讓讀者了解 getter, setter 的意義,筆者在這邊將 name 資料成員設為私用變數。

私用變數 (Private) 不能在宣告它的類別的外部訪問,所以不能繼承,也無法直接訪問。

什麼叫做直接訪問呢?

我們把 console.log(a.Name); 改成 console.log(a.name);試試看。

error: TS2341 [ERROR]: Property 'name' is private and only accessible within class 'Animal'.

這麼做會被提醒 name 是私用的,只有 Animal 類別有資格存取它。

注意 ! a.Name 是成員函數, a.name 則是類別名稱,可別搞混囉 !

靜態方法

  • static

    當在宣告時使用 static ,就代表我們宣告了一個靜態方法。

    靜態方法代表我們不需要將 class 實例化就能直接使用。

class Animal {    
    static isAnimal(a) {        
        return a instanceof Animal;    
    }
}
let a = new Animal('Jack');

呼叫 Animal 類別中的靜態方法:

Animal.isAnimal(a); // true

呼叫 a 實體中的靜態方法:

a.isAnimal(a); // TypeError: a.isAnimal is not a function

公用私用傻傻分不清楚?!

除了常用的公用變數 public 以外,在 TypeScript 中還有 private 以及 protected 可以使用。

種類

public

private

protected

內部存取

Yes

Yes

Yes

子類別繼承/訪問

Yes

No

Yes

實例化訪問

Yes

No

No

內部存取

使用成員函數 say() 取得資料成員 birthday 這樣的行為就是內部存取。

class Animal {
  public name: string;
  protected constructor(name: string) {
    this.name = name;
  }
  sayHi() {
    console.log(this.name);
  }
}
class Cat extends Animal {
  protected birthday: number;
  constructor(name: string) {
    super(name);
    this.birthday = 8;
  }
  say() {
    console.log(this.birthday);
  }
}
let a = new Cat("Jack");
a.say();

子類別繼承/訪問

class Animal {
  public name: string;
  protected constructor(name: string) {
    this.name = name;
  }
  sayHi() {
    console.log(this.name);
  }
}
class Cat extends Animal {
  protected birthday: number;
  constructor(name: string) {
    super(name);
    this.birthday = 8;
  }
  say() {
    console.log(this.birthday);
  }
}
let a = new Cat("Jack");
a.sayHi();

我們可以發現在 Cat 類別的宣告區塊中是沒有 sayHi 這項成員函數的。

let a = new Cat("Jack");
a.sayHi();

a.sayHi() 之所以能正常工作,是因為 Cat 類別繼承了 Animal 類別的 sayHi() 成員函數唷!

實例化訪問

class Animal {
  public name: string;
  protected constructor(name: string) {
    this.name = name;
  }
  sayHi() {
    console.log(this.name);
  }
}
class Cat extends Animal {
  protected birthday: number;
  constructor(name: string) {
    super(name);
    this.birthday = 8;
  }
  say() {
    console.log(this.birthday);
  }
}
let a = new Cat("Jack");
a.sayHi();
a.birthday;

我們在程式碼的末端加上一行 a.birthday ,來看看會發生什麼事吧!

error: TS2445 [ERROR]: Property 'birthday' is protected and only accessible within class 'Cat' and its subclasses.
a.birthday;

果不其然,筆者得到了錯誤訊息。

這樣的操作稱之為實例化訪問,筆者將 Cat 類別實例化後再訪問 a 實體的資料成員。

為什麼會出錯呢?

protected birthday: number;

範例中, birthday 是受保護的,代表它並不能被實例化存取。

如果讀者到這邊還不熟悉這幾項特性,可以透過嘗試修改範例程式碼親身體驗 : public, protected, private 的差異唷 !

Reference

有些文章的內容非常完善,如果有些部分筆者沒有更好的方法詮釋,便會以其他文章作為參考。

  • 筆者在本篇參考了它的範例程式碼,並以此作為基礎修改。

延伸閱讀

同樣的事情在不同人眼中可能會有不同的見解、看法。

在讀完本篇以後,筆者也強烈建議大家去看看以下文章,或許會對型別、變數宣告...等觀念有更深層的看法唷!

Previous物件導向概念Next介面與類別、抽象類別

Last updated 4 years ago

Was this helpful?

TypeScript 新手指南 - Class
ECMAScript 6 入門 - Class