TypeScript | 從 TS 開始學習物件導向 - Class 用法
前言
Hi!大家好,我是神 Q 超人。
最近一直在看「Head First Design Patterns」,害得現在只要看到以前的程式碼,都很有幹勁的想把它改成符合設計模式 😂 ,之後也會再把這些學習成果整理成文章。
但是在這之前,會需要了解「 Class (類別)」、「 Abstract class (抽象類別)」、「 Interface (介面)」等……這些名詞都代表什麼意思,之間又有哪些差異,就從 TypeScript 開始認識他們吧!
但是要注意哦!最終最終用 TypeScript 撰寫的程式都會被編譯成 JavaScript 實現,因此可別以為 TypeScript 讓 JavaScript 改變了
TypeScript 只是用語法省去 JavaScript 手動實現的過程,它仍然是 JavaScript 。
如果對 JavaScript 本身的 Prototype 還不是很清楚,建議可以先閱讀「 關於 Object ,一口氣全說完 」和「 ES6 中最容易誤會的語法糖 Class — 基本用法 」 🙌
Class 類別
類別就像一個設計圖,能透過它產生一個新物件
假設,今天汽車的廠商需要製造一台新車,那研發設計部門就會依照需求產生一張設計圖,並將設計圖送給廠商做外包處理,因為設計圖都相同所以做出來的零件也都相同。
在這個例子中「設計圖就是類別」、而根據設計圖生產的「零件就是物件」。
基本用法
承上方的例子,用 class
語法建立一張設計圖,裡面會有物件的資料,設計好後,使用 new
將物件建構出來,該物件就會擁有設計圖內的所有功能:
程式碼中,用 class
定義了類別 Car
,它描述了產出的物件會有 description
和 getDescription
兩個 Property (特性)。
Car
內有幾個值得注意的地方:
constructor
又稱為建構子,這個區塊用來定義 Property 的初始值,上方就在此區塊內定義了description
的 Value。public
是指讓該 Property 公開存取,也就是說,我能夠在建立出來的物件car1
或car2
上呼叫使用。
而像這樣子,透過 Car
產生的 car1
和 car2
,就可以說是 Car
的「 Instance (實體)」,因為按照設計圖將它實體化了嘛!
Constructor 建構子
詳細說明 Constructor 之前,汽車廠商的老闆有新想法,當這台新車按設計圖量產後,他覺得只有一種顏色的車子,無法吃到大眾市場,需要為車子增加幾種顏色。
但如果我們替所有顏色的同一車子都做個別的設計圖,會產生許多重複的程式碼,且當這台車因為當初設計不良,導致煞車出現問題,又要從每個設計圖中修改煞車功能,想就覺得是件費時又費力的事情。
這個狀況下 Constructor 就有作用了,透過 new
讓 Class 產生 Instance 的時後,可以傳入參數,而傳入的參數會被 Constructor 接收,
透過傳入不同的參數,就能讓 Instance 有些不同。
因為要增加顏色,所以修改上方的 Car
,替它增加一個 color
Property 作為顏色,並在 constructor
中以傳入的參數定義 color
的 Value:
Public & Private 公開和私有
處理完車子的顏色後,老闆相當滿意的繼續數鈔票,直到有一天,它發現市場上居然有同類型的車出現,才發現原來車子內部的技術都是公開的,沒有藏起來,所以敵對廠商買了車後,就把技術偷走了。
先說說之前設計的方式吧,可以看到 Car
裡面,定義了 description
、 color
、 getDescription
,在這之中只有 getDescription
被標記了 public
,也就是讓它成為公開方法,但另外兩個呢?原來一問之下才知道,
Class 內描述的 Property 預設是 Public
這也不能怪廠商,畢竟他們也不曉得什麼是不能公開的對吧?於是設計研發部門想了一個方式,在設計圖上註明這是商業機密,讓廠商知道不能公開,那就是 Private ,誰都別想取出標注了 Private 的 Property :
上方的程式會連編譯都無法編譯,雖然可以在 Class 內部使用,
但如果嘗試取出 Private 的 Property 會導致程式出錯
誰叫你要偷偷把我藏起來的東西打開,這可不是你該做的,該做的是享受用超機密方式煞車來帥一波,可別做些奇怪的事情。
inheritance 繼承
雖然造成一波虧損,但還是即時守護了其他功能免於被致敬,老闆依舊放心數著鈔票,但是被偷走一部份的技術還是讓他很氣憤,所以打算再改良煞車功能,讓自己的技術永遠領先別人一截。
看起來很棒,不過因為開發的價格過高了,如果只量產這個版本,買的人一定不多,於是老闆決定把這次改良作為第二代車型,第一代還是持續生產,而第二代則是擁有第一代的功能再加上新煞車技術的版本。
此時,設計研發團隊想到了,他們只需要撰寫第二代功能被加強的部分,其他需要第一代的部分就引用之前的設計圖,這樣子也不需要在第二代的設計圖上寫下已經記錄於前一張設計圖的設計:
可以發現,新增的 CarII
裡面,沒有原本在 Car
類別內的敘述,執行結果卻有 Car
的影子,因為上方用了 extends
定義了 CarII
的原型為 Car
,
新類別能夠以 extends 指定原型,而原型會賦予它身上的 Property 給新類別,這就叫繼承
被繼承的 CarII 也被稱作子類別,而賦予 Property 的 Car 則是父類別
CarII
從他老爸 Car
那裡繼承所有的 Method 和 Property ,且在類別內,只要透過 super
就能穿越父子間的羈絆,直接請老爸幫忙執行事情,就像上方的 super.getDescription
,就是直接請老爸執行了他的 getDescription
,因此重複的程式就不需要再打一遍,只需要在呼叫的前後撰寫新的程式碼功能就好。
另外如果父類別和子類別同時需要在 Constructor 定義的 Property ,就得使用 super
傳入父類別需要的值:
在子類別中執行的 super 等同於父類別的 constructor
Static 靜態
目前廠商拿到的設計圖有兩份,但之後會越來越多,有遠見的他們建議能不能在設計圖上標註一些容易辨別的註解,減少它們人力核對每台車的功能,這個註解不需要整台車造出來才能看見,而是透過設計圖本身就能確認。
優秀的設計研發團隊馬上想到了 Static ,
透過 Static 宣告的 Method 能直接在 Class 上呼叫,而不是 Instance 。
例子如下:
這麼一來,老闆便能繼續想東想西,廠商又能夠快速開發,研發團隊也能爽拿獎金, Class 真是個造福大家的好東西對吧!
但還沒結束哦!剩最後一個 Property 的種類要介紹。
Protected 保護
Protected 和 Private 很類似,差別在 Private 是私有的屬性,因此除了本身的類別外,便無法偷看裡面是什麼,否則就會出錯。
But ! 老爸對兒子的愛是寬容的,有些東西只防外人,自己人就不需要隱藏了,這時候
只需要將 Private 改成 Protected ,子類別便能存取被保護的 Property 了,
而在 Instance 中,仍然無法偷看到該 Property。
下方用簡單的例子詮釋 Protected :
如果要試上方的例子,記得將會出錯的地方刪去或註解掉,才能正確被編譯。
總結
本篇文章學習了以下幾點:
- Class 的基本用法。
- Property 擁有 Public 、 Private 、 Static 、Protected 四種屬性。
- Class 間的繼承。
這次是用 TypeScript 詮釋的類別寫法,目前看起來和 ES6 很像,但下一篇介紹的 「 Abstract class (抽象類別)」,就是原生 JS 還未有語法糖支持的功能,因此我們透過 TypeScript 簡單撰寫,再編譯就非常方便!不需要手動用原生 JS 完成那些麻煩的實現。
最後,不曉得是不是看太多 Head First Design Patterns 的書,這次的文章內容莫名的像書中的講解方式,如果覺得例子和廢話太多再麻煩留言告訴我 😂,我會再繼續調整文章的比重。
文章裡有任何問題,或是講解不清楚需要補充的地方,也請留言指教,感激不盡!