TypeScript系列(四):類

對于傳統(tǒng)的 JavaScript 程序我們會使用函數基于原型的繼承來創(chuàng)建可重用的組件僵刮,但對于熟悉使用面向對象方式的程序員使用這些語法就有些棘手瞎暑,因為他們用的是基于類的繼承并且對象是由類構建出來的讲衫。 從 ECMAScript 2015卖丸,也就是 ES6 開始畔濒, JavaScript 程序員將能夠使用基于類的面向對象的方式顽悼。 使用 TypeScript孝赫,我們允許開發(fā)者現(xiàn)在就使用這些特性较木,并且編譯后的 JavaScript 可以在所有主流瀏覽器和平臺上運行,而不需要等到下個 JavaScript 版本青柄。

基本示例

下面看一個使用類的例子:

/* 
類的基本定義與使用
*/

class Greeter {
  // 聲明屬性
  message: string

  // 構造方法
  constructor (message: string) {
    this.message = message
  }

  // 一般方法
  greet (): string {
    return 'Hello ' + this.message
  }
}

// 創(chuàng)建類的實例
const greeter = new Greeter('world')
// 調用實例的方法
console.log(greeter.greet())

如果你使用過 C# 或 Java伐债,你會對這種語法非常熟悉。 我們聲明一個 Greeter 類致开。這個類有 3 個成員:一個叫做 message 的屬性峰锁,一個構造函數和一個 greet 方法。

你會注意到双戳,我們在引用任何一個類成員的時候都用了 this虹蒋。 它表示我們訪問的是類的成員。

后面一行飒货,我們使用 new 構造了 Greeter 類的一個實例魄衅。它會調用之前定義的構造函數,創(chuàng)建一個 Greeter 類型的新對象塘辅,并執(zhí)行構造函數初始化它晃虫。

最后一行通過 greeter 對象調用其 greet 方法

繼承

在 TypeScript 里浓镜,我們可以使用常用的面向對象模式肥败。 基于類的程序設計中一種最基本的模式是允許使用繼承來擴展現(xiàn)有的類栈妆。

看下面的例子:

/* 
類的繼承
*/

class Animal {
  run (distance: number) {
    console.log(`Animal run ${distance}m`)
  }
}

class Dog extends Animal {
  cry () {
    console.log('wang! wang!')
  }
}

const dog = new Dog()
dog.cry() 
dog.run(100) // 可以調用從父中繼承得到的方法

這個例子展示了最基本的繼承:類從基類中繼承了屬性和方法初肉。 這里,Dog 是一個 派生類荆责,它派生自 Animal 基類滥比,通過 extends 關鍵字。 派生類通常被稱作子類草巡,基類通常被稱作超類守呜。

因為 Dog 繼承了 Animal 的功能,因此我們可以創(chuàng)建一個 Dog 的實例山憨,它能夠 cry()run()查乒。

下面我們來看個更加復雜的例子:

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

  run (distance: number=0) {
    console.log(`${this.name} run ${distance}m`)
  }

}

class Snake extends Animal {
  constructor (name: string) {
    // 調用父類型構造方法
    super(name)
  }

  // 重寫父類型的方法
  run (distance: number=5) {
    console.log('sliding...')
    super.run(distance)
  }
}

class Horse extends Animal {
  constructor (name: string) {
    // 調用父類型構造方法
    super(name)
  }

  // 重寫父類型的方法
  run (distance: number=50) {
    console.log('dashing...')
    // 調用父類型的一般方法
    super.run(distance)
  }

  xxx () {
    console.log('xxx()')
  }
}

const snake = new Snake('sn')
snake.run()

const horse = new Horse('ho')
horse.run()

// 父類型引用指向子類型的實例 ==> 多態(tài)
const tom: Animal = new Horse('ho22')
tom.run()

/* 如果子類型沒有擴展的方法, 可以讓子類型引用指向父類型的實例 */
const tom3: Snake = new Animal('tom3')
tom3.run()
/* 如果子類型有擴展的方法, 不能讓子類型引用指向父類型的實例 */
// const tom2: Horse = new Animal('tom2')
// tom2.run()

這個例子展示了一些上面沒有提到的特性。 這一次郁竟,我們使用 extends 關鍵字創(chuàng)建了 Animal 的兩個子類:HorseSnake玛迄。

與前一個例子的不同點是,派生類包含了一個構造函數棚亩,它 必須調用 super()蓖议,它會執(zhí)行基類的構造函數。 而且讥蟆,在構造函數里訪問 this 的屬性之前勒虾,我們 一定要調用 super()。 這個是 TypeScript 強制執(zhí)行的一條重要規(guī)則瘸彤。

這個例子演示了如何在子類里可以重寫父類的方法修然。Snake類和 Horse 類都創(chuàng)建了 run 方法,它們重寫了從 Animal 繼承來的 run 方法质况,使得 run 方法根據不同的類而具有不同的功能愕宋。注意,即使 tom 被聲明為 Animal 類型结榄,但因為它的值是 Horse中贝,調用 tom.run(34) 時,它會調用 Horse 里重寫的方法臼朗。

sliding...
sn run 5m
dashing...
ho run 50m

修飾符

默認為 public 修飾符
在上面的例子里邻寿,我們可以自由的訪問程序里定義的成員。 如果你對其它語言中的類比較了解依溯,就會注意到我們在之前的代碼里并沒有使用 public 來做修飾老厌;例如,C# 要求必須明確地使用 public 指定成員是可見的黎炉。 在 TypeScript 里枝秤,成員都默認為 public

你也可以明確的將一個成員標記成 public慷嗜。 我們可以用下面的方式來重寫上面的 Animal 類:
private 修飾符
當成員被標記成 private 時淀弹,它就不能在聲明它的類的外部訪問丹壕。
protected 修飾符
protected 修飾符與 private 修飾符的行為很相似,但有一點不同薇溃,protected成員在派生類中仍然可以訪問菌赖。例如:

/* 
訪問修飾符: 用來描述類內部的屬性/方法的可訪問性
  public: 默認值, 公開的外部也可以訪問
  private: 只能類內部可以訪問
  protected: 類內部和子類可以訪問
*/

class Animal {
  public name: string

  public constructor (name: string) {
    this.name = name
  }

  public run (distance: number=0) {
    console.log(`${this.name} run ${distance}m`)
  }
}

class Person extends Animal {
  private age: number = 18
  protected sex: string = '男'

  run (distance: number=5) {
    console.log('Person jumping...')
    super.run(distance)
  }
}

class Student extends Person {
  run (distance: number=6) {
    console.log('Student jumping...')

    console.log(this.sex) // 子類能看到父類中受保護的成員
    // console.log(this.age) //  子類看不到父類中私有的成員

    super.run(distance)
  }
}

console.log(new Person('abc').name) // 公開的可見
// console.log(new Person('abc').sex) // 受保護的不可見
// console.log(new Person('abc').age) //  私有的不可見

readonly 修飾符
你可以使用 readonly 關鍵字將屬性設置為只讀的。 只讀屬性必須在聲明時或構造函數里被初始化沐序。

class Person {
  readonly name: string = 'abc'
  constructor(name: string) {
    this.name = name
  }
}

let john = new Person('John')
// john.name = 'peter' // error

參數屬性

在上面的例子中琉用,我們必須在 Person 類里定義一個只讀成員 name 和一個參數為 name 的構造函數,并且立刻將 name 的值賦給 this.name策幼,這種情況經常會遇到邑时。 參數屬性可以方便地讓我們在一個地方定義并初始化一個成員。 下面的例子是對之前 Person 類的修改版特姐,使用了參數屬性:

class Person2 {
  constructor(readonly name: string) {
  }
}

const p = new Person2('jack')
console.log(p.name)

注意看我們是如何舍棄參數 name晶丘,僅在構造函數里使用 readonly name: string 參數來創(chuàng)建和初始化 name 成員。 我們把聲明和賦值合并至一處唐含。

參數屬性通過給構造函數參數前面添加一個訪問限定符來聲明浅浮。使用 private 限定一個參數屬性會聲明并初始化一個私有成員;對于 publicprotected 來說也是一樣捷枯。

存取器

TypeScript 支持通過 getters/setters 來截取對對象成員的訪問滚秩。 它能幫助你有效的控制對對象成員的訪問。

下面來看如何把一個簡單的類改寫成使用 getset淮捆。 首先叔遂,我們從一個沒有使用存取器的例子開始。

class Person {
  firstName: string = 'A'
  lastName: string = 'B'
  get fullName () {
    return this.firstName + '-' + this.lastName
  }
  set fullName (value) {
    const names = value.split('-')
    this.firstName = names[0]
    this.lastName = names[1]
  }
}

const p = new Person()
console.log(p.fullName)

p.firstName = 'C'
p.lastName =  'D'
console.log(p.fullName)

p.fullName = 'E-F'
console.log(p.firstName, p.lastName)

靜態(tài)屬性

到目前為止争剿,我們只討論了類的實例成員,那些僅當類被實例化的時候才會被初始化的屬性痊末。 我們也可以創(chuàng)建類的靜態(tài)成員蚕苇,這些屬性存在于類本身上面而不是類的實例上。 在這個例子里凿叠,我們使用 static 定義 origin涩笤,因為它是所有網格都會用到的屬性。 每個實例想要訪問這個屬性的時候盒件,都要在 origin 前面加上類名蹬碧。 如同在實例屬性上使用 this.xxx 來訪問屬性一樣,這里我們使用 Grid.xxx 來訪問靜態(tài)屬性炒刁。

/* 
靜態(tài)屬性, 是類對象的屬性
非靜態(tài)屬性, 是類的實例對象的屬性
*/

class Person {
  name1: string = 'A'
  static name2: string = 'B'
}

console.log(Person.name2)
console.log(new Person().name1)

抽象類

抽象類做為其它派生類的基類使用恩沽。 它們不能被實例化。不同于接口翔始,抽象類可以包含成員的實現(xiàn)細節(jié)罗心。 abstract 關鍵字是用于定義抽象類和在抽象類內部定義抽象方法里伯。

/* 
抽象類
  不能創(chuàng)建實例對象, 只有實現(xiàn)類才能創(chuàng)建實例
  可以包含未實現(xiàn)的抽象方法
*/

abstract class Animal {

  abstract cry ()

  run () {
    console.log('run()')
  }
}

class Dog extends Animal {
  cry () {
    console.log(' Dog cry()')
  }
}

const dog = new Dog()
dog.cry()
dog.run()
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市渤闷,隨后出現(xiàn)的幾起案子疾瓮,更是在濱河造成了極大的恐慌,老刑警劉巖飒箭,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狼电,死亡現(xiàn)場離奇詭異,居然都是意外死亡弦蹂,警方通過查閱死者的電腦和手機肩碟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盈匾,“玉大人腾务,你說我怎么就攤上這事∠鞫” “怎么了岩瘦?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窿撬。 經常有香客問我启昧,道長,這世上最難降的妖魔是什么劈伴? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任密末,我火速辦了婚禮,結果婚禮上跛璧,老公的妹妹穿的比我還像新娘严里。我一直安慰自己,他們只是感情好追城,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布刹碾。 她就那樣靜靜地躺著,像睡著了一般座柱。 火紅的嫁衣襯著肌膚如雪迷帜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天色洞,我揣著相機與錄音戏锹,去河邊找鬼。 笑死火诸,一個胖子當著我的面吹牛锦针,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼伞插,長吁一口氣:“原來是場噩夢啊……” “哼割粮!你這毒婦竟也來了?” 一聲冷哼從身側響起媚污,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤舀瓢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后耗美,有當地人在樹林里發(fā)現(xiàn)了一具尸體京髓,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年商架,在試婚紗的時候發(fā)現(xiàn)自己被綠了堰怨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛇摸,死狀恐怖备图,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情赶袄,我是刑警寧澤揽涮,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站饿肺,受9級特大地震影響蒋困,放射性物質發(fā)生泄漏。R本人自食惡果不足惜敬辣,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一雪标、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溉跃,春花似錦村刨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乾吻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拟蜻,已是汗流浹背绎签。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酝锅,地道東北人诡必。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親爸舒。 傳聞我的和親對象是個殘疾皇子蟋字,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • 類的介紹 ES6之前Javascript是使用函數和基于原型的繼承來實現(xiàn)類的概念。而在Typescript中則可以...
    克里斯先生20閱讀 340評論 0 0
  • 本文介紹TypeScript集面向對象抽象扭勉、封裝鹊奖、多態(tài)三要素為一體的編程利器,類類型涂炎。 在JavaScript(E...
    金融測試民工閱讀 1,187評論 0 0
  • 傳統(tǒng)的JavaScript程序使用函數和基于原型的繼承來創(chuàng)建可重用的組件忠聚,但對于熟悉使用面向對象方式的程序員來講就...
    2o壹9閱讀 610評論 0 50
  • 下面看一個使用類的例子: 我們聲明一個Greeter類。這個類有3個成員:一個叫做greeting的屬性唱捣,一個構造...
    oWSQo閱讀 380評論 0 1
  • 類 對于傳統(tǒng)的 JavaScript 程序我們會使用函數和基于原型的繼承來創(chuàng)建可重用的組件两蟀,但對于熟悉使用面向對象...
    羅彬727閱讀 218評論 0 0