面試官:說(shuō)說(shuō)你對(duì) TypeScript 中類(lèi)的理解?應(yīng)用場(chǎng)景?
本文轉(zhuǎn)載自微信公眾號(hào)「JS每日一題」,作者灰灰。轉(zhuǎn)載本文請(qǐng)聯(lián)系JS每日一題公眾號(hào)。
一、是什么
類(lèi)(Class)是面向?qū)ο蟪绦蛟O(shè)計(jì)(OOP,Object-Oriented Programming)實(shí)現(xiàn)信息封裝的基礎(chǔ)
類(lèi)是一種用戶(hù)定義的引用數(shù)據(jù)類(lèi)型,也稱(chēng)類(lèi)類(lèi)型
傳統(tǒng)的面向?qū)ο笳Z(yǔ)言基本都是基于類(lèi)的,JavaScript 基于原型的方式讓開(kāi)發(fā)者多了很多理解成本
在 ES6 之后,JavaScript 擁有了 class 關(guān)鍵字,雖然本質(zhì)依然是構(gòu)造函數(shù),但是使用起來(lái)已經(jīng)方便了許多
但是JavaScript 的class依然有一些特性還沒(méi)有加入,比如修飾符和抽象類(lèi)
TypeScript 的 class 支持面向?qū)ο蟮乃刑匦?,比?類(lèi)、接口等
二、使用方式
定義類(lèi)的關(guān)鍵字為 class,后面緊跟類(lèi)名,類(lèi)可以包含以下幾個(gè)模塊(類(lèi)的數(shù)據(jù)成員):
- 「字段」 :字段是類(lèi)里面聲明的變量。字段表示對(duì)象的有關(guān)數(shù)據(jù)。
- 「構(gòu)造函數(shù)」:類(lèi)實(shí)例化時(shí)調(diào)用,可以為類(lèi)的對(duì)象分配內(nèi)存。
- 「方法」:方法為對(duì)象要執(zhí)行的操作
如下例子:
- class Car {
- // 字段
- engine:string;
- // 構(gòu)造函數(shù)
- constructor(engine:string) {
- this.engine = engine
- }
- // 方法
- disp():void {
- console.log("發(fā)動(dòng)機(jī)為 : "+this.engine)
- }
- }
繼承
類(lèi)的繼承使用過(guò)extends的關(guān)鍵字
- class Animal {
- move(distanceInMeters: number = 0) {
- console.log(`Animal moved ${distanceInMeters}m.`);
- }
- }
- class Dog extends Animal {
- bark() {
- console.log('Woof! Woof!');
- }
- }
- const dog = new Dog();
- dog.bark();
- dog.move(10);
- dog.bark();
Dog是一個(gè) 派生類(lèi),它派生自 Animal 基類(lèi),派生類(lèi)通常被稱(chēng)作子類(lèi),基類(lèi)通常被稱(chēng)作 超類(lèi)
Dog類(lèi)繼承了Animal類(lèi),因此實(shí)例dog也能夠使用Animal類(lèi)move方法
同樣,類(lèi)繼承后,子類(lèi)可以對(duì)父類(lèi)的方法重新定義,這個(gè)過(guò)程稱(chēng)之為方法的重寫(xiě),通過(guò)super關(guān)鍵字是對(duì)父類(lèi)的直接引用,該關(guān)鍵字可以引用父類(lèi)的屬性和方法,如下:
- class PrinterClass {
- doPrint():void {
- console.log("父類(lèi)的 doPrint() 方法。")
- }
- }
- class StringPrinter extends PrinterClass {
- doPrint():void {
- super.doPrint() // 調(diào)用父類(lèi)的函數(shù)
- console.log("子類(lèi)的 doPrint()方法。")
- }
- }
修飾符
可以看到,上述的形式跟ES6十分的相似,typescript在此基礎(chǔ)上添加了三種修飾符:
- 公共 public:可以自由的訪問(wèn)類(lèi)程序里定義的成員
- 私有 private:只能夠在該類(lèi)的內(nèi)部進(jìn)行訪問(wèn)
- 受保護(hù) protect:除了在該類(lèi)的內(nèi)部可以訪問(wèn),還可以在子類(lèi)中仍然可以訪問(wèn)
私有修飾符
只能夠在該類(lèi)的內(nèi)部進(jìn)行訪問(wèn),實(shí)例對(duì)象并不能夠訪問(wèn)
并且繼承該類(lèi)的子類(lèi)并不能訪問(wèn),如下圖所示:
受保護(hù)修飾符
跟私有修飾符很相似,實(shí)例對(duì)象同樣不能訪問(wèn)受保護(hù)的屬性,如下:
有一點(diǎn)不同的是 protected 成員在子類(lèi)中仍然可以訪問(wèn)
除了上述修飾符之外,還有只讀「修飾符」
只讀修飾符
通過(guò)readonly關(guān)鍵字進(jìn)行聲明,只讀屬性必須在聲明時(shí)或構(gòu)造函數(shù)里被初始化,如下:
除了實(shí)例屬性之外,同樣存在靜態(tài)屬性
靜態(tài)屬性
這些屬性存在于類(lèi)本身上面而不是類(lèi)的實(shí)例上,通過(guò)static進(jìn)行定義,訪問(wèn)這些屬性需要通過(guò) 類(lèi)型.靜態(tài)屬性 的這種形式訪問(wèn),如下所示:
- class Square {
- static width = '100px'
- }
- console.log(Square.width) // 100px
上述的類(lèi)都能發(fā)現(xiàn)一個(gè)特點(diǎn)就是,都能夠被實(shí)例化,在 typescript中,還存在一種抽象類(lèi)
抽象類(lèi)
抽象類(lèi)做為其它派生類(lèi)的基類(lèi)使用,它們一般不會(huì)直接被實(shí)例化,不同于接口,抽象類(lèi)可以包含成員的實(shí)現(xiàn)細(xì)節(jié)
abstract關(guān)鍵字是用于定義抽象類(lèi)和在抽象類(lèi)內(nèi)部定義抽象方法,如下所示:
- abstract class Animal {
- abstract makeSound(): void;
- move(): void {
- console.log('roaming the earch...');
- }
- }
這種類(lèi)并不能被實(shí)例化,通常需要我們創(chuàng)建子類(lèi)去繼承,如下:
- class Cat extends Animal {
- makeSound() {
- console.log('miao miao')
- }
- }
- const cat = new Cat()
- cat.makeSound() // miao miao
- cat.move() // roaming the earch...
三、應(yīng)用場(chǎng)景
除了日常借助類(lèi)的特性完成日常業(yè)務(wù)代碼,還可以將類(lèi)(class)也可以作為接口,尤其在 React 工程中是很常用的,如下:
export default class Carousel extends React.Component
由于組件需要傳入 props 的類(lèi)型 Props ,同時(shí)有需要設(shè)置默認(rèn) props 即 defaultProps,這時(shí)候更加適合使用class作為接口
先聲明一個(gè)類(lèi),這個(gè)類(lèi)包含組件 props 所需的類(lèi)型和初始值:
- // props的類(lèi)型
- export default class Props {
- public children: Array<React.ReactElement<any>> | React.ReactElement<any> | never[] = []
- public speed: number = 500
- public height: number = 160
- public animation: string = 'easeInOutQuad'
- public isAuto: boolean = true
- public autoPlayInterval: number = 4500
- public afterChange: () => {}
- public beforeChange: () => {}
- public selesctedColor: string
- public showDots: boolean = true
- }
當(dāng)我們需要傳入 props 類(lèi)型的時(shí)候直接將 Props 作為接口傳入,此時(shí) Props 的作用就是接口,而當(dāng)需要我們?cè)O(shè)置defaultProps初始值的時(shí)候,我們只需要:
- public static defaultProps = new Props()
Props 的實(shí)例就是 defaultProps 的初始值,這就是 class作為接口的實(shí)際應(yīng)用,我們用一個(gè) class 起到了接口和設(shè)置初始值兩個(gè)作用,方便統(tǒng)一管理,減少了代碼量
參考文獻(xiàn)
https://www.tslang.cn/docs/handbook/classes.html
https://www.runoob.com/typescript/ts-class.html