對(duì)于傳統(tǒng)的 JavaScript 程序我們會(huì)使用函數(shù)
和基于原型的繼承
來(lái)創(chuàng)建可重用的組件,但對(duì)于熟悉使用面向?qū)ο蠓绞降某绦騿T使用這些語(yǔ)法就有些棘手,因?yàn)樗麄冇玫氖腔陬惖睦^承并且對(duì)象是由類構(gòu)建出來(lái)的。 從 ECMAScript 2015,也就是 ES6 開(kāi)始, JavaScript 程序員將能夠使用基于類的面向?qū)ο蟮姆绞健?使用 TypeScript,我們?cè)试S開(kāi)發(fā)者現(xiàn)在就使用這些特性,并且編譯后的 JavaScript 可以在所有主流瀏覽器和平臺(tái)上運(yùn)行,而不需要等到下個(gè) JavaScript 版本。
成都創(chuàng)新互聯(lián)公司長(zhǎng)期為成百上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為郎溪企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站,郎溪網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
// 類
(() => {
class Person {
// 聲明屬性
name: string
age: number
gender: string
// 構(gòu)造方法
constructor(name: string='jkc', age:number=18, gender:string='男') {
this.name = name
this.age = age
this.gender = gender
}
// 一般方法
sayHi(str: string){
console.log(`你好,我叫${this.name},今年${this.age}歲,性別${this.gender}, 我想說(shuō):`, str)
}
}
// 創(chuàng)建類的實(shí)例
const person = new Person()
// 調(diào)用實(shí)例的方法
person.sayHi('我很帥')
})()
如果你使用過(guò)C#或Java,你會(huì)對(duì)這種語(yǔ)法非常熟悉。我們聲明了一個(gè)Person
類。這個(gè)類有3個(gè)屬性、一個(gè)構(gòu)造函數(shù)和一個(gè)sayHi
方法。
我們使用new
構(gòu)造了Person
類的一個(gè)實(shí)例。它會(huì)調(diào)用構(gòu)造函數(shù),創(chuàng)建一個(gè)Person
類型的新對(duì)象,并執(zhí)行構(gòu)造函數(shù)初始化它。最后通過(guò)person
對(duì)象調(diào)用其sayHi
方法
在 TypeScript 里,我們可以使用常用的面向?qū)ο竽J健?基于類的程序設(shè)計(jì)中一種最基本的模式是允許使用繼承來(lái)擴(kuò)展現(xiàn)有的類。
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) {
// 調(diào)用父類型構(gòu)造方法
super(name)
}
// 重寫父類的方法
run (distance: number=5) {
console.log('sliding...')
super.run(distance)
}
}
class Horse extends Animal {
constructor (name: string) {
// 調(diào)用父類型構(gòu)造方法
super(name)
}
// 重寫父類型的方法
run (distance: number=50) {
console.log('dashing...')
// 調(diào)用父類型的一般方法
super.run(distance)
}
xxx () {
console.log('xxx()')
}
}
const snake = new Snake('sn')
snake.run()
const horse = new Horse('ho')
horse.run()
我們定義了一個(gè)超類Animal,兩個(gè)派生類Snake和Horse,并且創(chuàng)建了2個(gè)實(shí)例對(duì)象snake
和horse
。
通過(guò)snake.run()
,我們可以看到Snake中有run方法,那么就進(jìn)行調(diào)用,最后結(jié)果如下
通過(guò)horse.run()
,我們可以看到Horse中有run方法,那么進(jìn)行調(diào)用,最后結(jié)果如下:
定義:不同類型的對(duì)象針對(duì)相同的方法,產(chǎn)生了不同的的行為
接著上面的代碼
// 父類型引用指向子類型的實(shí)例 ==> 多態(tài)
const tom: Animal = new Horse('ho22')
tom.run()
/* 如果子類型沒(méi)有擴(kuò)展的方法, 可以讓子類型引用指向父類型的實(shí)例 */
const tom3: Snake = new Animal('tom3')
tom3.run()
/* 如果子類型有擴(kuò)展的方法, 不能讓子類型引用指向父類型的實(shí)例 */
const tom2: Horse = new Animal('tom2')
tom2.run()
這個(gè)例子演示了如何在子類里可以重寫父類的方法。Snake
類和 Horse
類都創(chuàng)建了 run
方法,它們重寫了從 Animal
繼承來(lái)的 run
方法,使得 run
方法根據(jù)不同的類而具有不同的功能。注意,即使 tom
被聲明為 Animal
類型,但因?yàn)樗闹凳?Horse
,調(diào)用 tom.run(34)
時(shí),它會(huì)調(diào)用 Horse
里重寫的方法。
在上面的例子里,我們可以自由的訪問(wèn)程序里定義的成員。 如果你對(duì)其它語(yǔ)言中的類比較了解,就會(huì)注意到我們?cè)谥暗拇a里并沒(méi)有使用 public
來(lái)做修飾;例如,C#要求必須明確地使用 public
指定成員是可見(jiàn)的。 在TypeScript
里,成員都默認(rèn)為 public
。
你也可以明確的將一個(gè)成員標(biāo)記成 public
。 我們可以用下面的方式來(lái)重寫上面的 Animal
類:
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
當(dāng)成員被標(biāo)記成 private時(shí),它就不能在聲明它的類的外部訪問(wèn)。比如:
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // 錯(cuò)誤: 'name' 是私有的.
protected
修飾符與 private
修飾符的行為很相似,但有一點(diǎn)不同,protected
成員在派生類中仍然可以訪問(wèn)。例如
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) // 子類能看到父類中受保護(hù)的成員
// console.log(this.age) // 子類看不到父類中私有的成員
super.run(distance)
}
}
console.log(new Person('abc').name) // 公開(kāi)的可見(jiàn)
// console.log(new Person('abc').sex) // 受保護(hù)的不可見(jiàn)
// console.log(new Person('abc').age) // 私有的不可見(jiàn)
你可以使用 readonly
關(guān)鍵字將屬性設(shè)置為只讀的。 只讀屬性必須在聲明時(shí)或構(gòu)造函數(shù)里被初始化。
class Person {
readonly name: string = 'abc'
constructor(name: string) {
this.name = name
}
}
let john = new Person('John')
// john.name = 'peter' // error
在上面的例子中,我們必須在 Person 類里定義一個(gè)只讀成員 name 和一個(gè)參數(shù)為 name 的構(gòu)造函數(shù),并且立刻將 name
的值賦給 this.name
,這種情況經(jīng)常會(huì)遇到。 參數(shù)屬性可以方便地讓我們?cè)谝粋€(gè)地方定義并初始化一個(gè)成員。 下面的例子是對(duì)之前 Person 類的修改版,使用了參數(shù)屬性
class Person2 {
constructor(readonly name: string) {
}
}
const p = new Person2('jack')
console.log(p.name)
注意看我們是如何舍棄參數(shù) name
,僅在構(gòu)造函數(shù)里使用 readonly name: string
參數(shù)來(lái)創(chuàng)建和初始化 name
成員。 我們把聲明和賦值合并至一處。
參數(shù)屬性通過(guò)給構(gòu)造函數(shù)參數(shù)前面添加一個(gè)訪問(wèn)限定符來(lái)聲明。使用 private
限定一個(gè)參數(shù)屬性會(huì)聲明并初始化一個(gè)私有成員;對(duì)于 public
和 protected
來(lái)說(shuō)也是一樣。
TypeScript
支持通過(guò) getters/setters
來(lái)截取對(duì)對(duì)象成員的訪問(wèn)。 它能幫助你有效的控制對(duì)對(duì)象成員的訪問(wèn)。
下面來(lái)看如何把一個(gè)簡(jiǎn)單的類改寫成使用 get
和 set
。 首先,我們從一個(gè)沒(méi)有使用存取器的例子開(kāi)始。
class P{
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 P()
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)成員:在類中通過(guò)static
修飾的屬性或方法,也就是靜態(tài)成員或靜態(tài)方法,靜態(tài)成員在使用時(shí)是通過(guò)類名.
的這種語(yǔ)法來(lái)調(diào)用
class People{
static name1: string = 'jkc'
// 構(gòu)造函數(shù)是不能通過(guò)static修飾的
constructor() {
}
static sayHi() {
console.log("hello")
}
}
People.name1 = 'jkc2'
console.log(People.name1)
People.sayHi()
抽象類:包含抽象方法(抽象方法一般沒(méi)有任何具體的內(nèi)容的實(shí)現(xiàn)),也可以包含實(shí)例方法,抽象類是不能被實(shí)例化,為了讓子類進(jìn)行實(shí)例化及實(shí)現(xiàn)內(nèi)部的抽象方法。
abstract class P1 {
// 抽象方法不能有具體的實(shí)現(xiàn)代碼
abstract eat()
sayHi() {
console.log('hello')
}
}
class P2 extends P1 {
eat() {
// 重新實(shí)現(xiàn)抽象類中的方法,此時(shí)這個(gè)方式是P2的實(shí)例方法
console.log("吃東西")
}
}
const p2 = new P2()
p2.eat()