日期: 2019 年 9 月2 日
類
類的例子
/**
* @description 類
* @class Greeter
*/
class Greeter{
greeting: string;
constructor(message: string){
this.greeting = message;
}
greet(){
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
console.log(greeter.greet());
繼承
基于類的程序設(shè)計中一種最基本的模式是允許使用繼承來擴展現(xiàn)有的類
/**
* @description 類的繼承
* @class Animal
*/
class Animal{
move(distance: number= 0): void{
console.log(`Animal moved ${ distance } meters.`)
}
}
class Dog extends Animal{
bark(){
console.log("Woof,wang wang...");
}
}
let dog = new Dog();
dog.bark();
dog.move();
dog.move(10);
這個例子展示了最基本的繼承:類從基類中繼承了屬性和方法。 這里, Dog是一個 派生類,它派生自 Animal 基類,通過 extends關(guān)鍵字。 派生類通常被稱作 子類,基類通常被稱作 超類
一個更加復(fù)雜的類繼承的例子:
/**
* @description 復(fù)雜的類繼承的例子
* @class Animals
*/
class Animals{
name: string;
constructor(myName: string){
this.name = myName;
}
move(distance: number= 22){
console.log(`${this.name} moved ${ distance } meters.`);
}
}
class Horse extends Animals{
constructor(name: string){
super(name);
}
move(distance: number= 233){
console.log('Horse, horse..');
super.move(distance);
}
}
class Cat extends Animals{
constructor(name: string){
super(name);
}
move(distance: number= 23){
console.log("喵喵喵...");
super.move(distance);
}
}
let horse = new Horse("black Horse");
horse.move();
horse.move(222);
let cat = new Cat("little cat");
cat.move();
cat.move(123);
這個例子演示了如何在子類里可以重寫父類的方法
公有、私有與受保護的修飾符
默認為 public
在TypeScript里,成員都默認為 public。你也可以明確的將一個成員標(biāo)記成 public
/**
* @description typescript 中,成員默認都是 public
* @class Haha
*/
class Haha{
public name: string;
public constructor(theName: string){
this.name = theName;
}
public greet(){
console.log(`Hello, ${ this.name }`);
}
}
let haha = new Haha("Lee");
console.log(haha.name);
私有 private
當(dāng)成員被標(biāo)記成 private時,它就不能在聲明它的類的外部訪問
/**
* @description 當(dāng)成員被標(biāo)記成 private 時,就不能在聲明它的類外訪問它
* @class Hahah
*/
class Hahah{
private name: string;
constructor(theName: string){
this.name = theName;
}
printName(){
console.log(this.name);
}
}
class Hah extends Hahah{
constructor(name: string){
super(name);
}
// sayHello(){ // error
// console.log(`Hello, ${ this.name }`)
// }
}
let ha = new Hahah('hello');
ha.printName();
// ha.name; // error
TypeScript使用的是結(jié)構(gòu)性類型系統(tǒng)。 當(dāng)我們比較兩種不同的類型時,并不在乎它們從何處而來,如果所有成員的類型都是兼容的,我們就認為它們的類型是兼容的,當(dāng)我們比較帶有 private或 protected成員的類型的時候,情況就不同了。 如果其中一個類型里包含一個 private成員,那么只有當(dāng)另外一個類型中也存在這樣一個 private成員, 并且它們都是來自同一處聲明時,我們才認為這兩個類型是兼容的。 對于 protected成員也使用這個規(guī)則。
/**
* @description 如果其中一個類型里包含一個 private成員,
* 那么只有當(dāng)另外一個類型中也存在這樣一個 private成員,
* 并且它們都是來自同一處聲明時,我們才認為這兩個類型是兼容的
* @class Fruit
*/
class Fruit{
private name: string;
constructor(theName: string){
this.name = theName;
}
}
class Apple extends Fruit{
constructor(){
super("apple");
}
}
class Employe{
private name: string;
constructor(theName: string){
this.name = theName;
}
}
let fruit = new Fruit("pine-apple");
let apple = new Apple();
let employe = new Employe("Tom");
fruit = apple;
// fruit = employe; // error
protected
protected修飾符與 private修飾符的行為很相似,但有一點不同, protected成員在派生類中仍然可以訪問。例如:
/**
* @description 當(dāng)成員被標(biāo)記成 protected 時,只能在類及其子類中訪問,在此之外不能訪問
* @class Person
*/
class Person{
protected name: string;
constructor(theName: string){
this.name = theName;
}
}
class Staff extends Person{
private dpt: string;
constructor(name: string, dpt: string){
super(name);
this.dpt = dpt;
}
greet(){
console.log(`Hello, my name is ${ this.name } and I work in ${ this.dpt }.`);
}
}
let hand = new Staff("hanpi", "technology");
hand.greet;
// console.log(hand. name); // error
readonly 修飾符
你可以使用 readonly關(guān)鍵字將屬性設(shè)置為只讀的,只讀屬性必須在聲明時或構(gòu)造函數(shù)里被初始化
/**
* @description readonly 修飾符
* @class Octopus
*/
class Octopus{
readonly name: string;
constructor(theName: string){
this.name = theName;
}
}
let octo = new Octopus("yaya");
// octo.name = "lala"; // error
參數(shù)屬性
參數(shù)屬性通過給構(gòu)造函數(shù)參數(shù)前面添加一個訪問限定符來聲明。 使用 private限定一個參數(shù)屬性會聲明并初始化一個私有成員;對于 public和 protected來說也是一樣
/**
* @description 參數(shù)屬性
* @class Octo
*/
class Octo{
constructor(readonly name: string){ }
}
let a = new Octo("lala");
存取器
TypeScript支持通過getters/setters來截取對對象成員的訪問。 它能幫助你有效的控制對對象成員的訪問
let passcode = "secret passcode";
/**
* @description 存取器
* @class Employes
*/
class Employes{
private _fullName: string = "lalala";
get fullName(): string{
return this._fullName;
}
set fullName(newName: string){
if(passcode&&passcode=="secret passcode"){
this._fullName = newName;
}else{
console.log("Error: Unauthorized update of Employes.");
}
}
}
let man = new Employes();
console.log(man.fullName);
man.fullName = "hahah";
console.log(man.fullName);
對于存取器有下面幾點需要注意的:首先,存取器要求你將編譯器設(shè)置為輸出ECMAScript 5或更高。 不支持降級到ECMAScript 3。 其次,只帶有 get不帶有 set的存取器自動被推斷為 readonly。 這在從代碼生成 .d.ts文件時是有幫助的,因為利用這個屬性的用戶會看到不允許夠改變它的值。
靜態(tài)屬性
到目前為止,我們只討論了類的實例成員,那些僅當(dāng)類被實例化的時候才會被初始化的屬性。 我們也可以創(chuàng)建類的靜態(tài)成員,這些屬性存在于類本身上面而不是類的實例上
/**
* @description 靜態(tài)屬性, 類的靜態(tài)成員,這些屬性存在于類本身上面而不是類的實例上
* @class Grid
*/
class Grid{
static origin= { x: 0, y: 0 };
calculateDistance( point: { x: number, y: number }){
let xDis = point.x - Grid.origin.x;
let yDis = point.y - Grid.origin.y;
return Math.sqrt(xDis*xDis + yDis*yDis) / this.scale;
}
constructor(public scale: number){}
}
let grid1 = new Grid(1);
console.log(grid1.calculateDistance({ x: 10, y: 10 }));
抽象類
抽象類做為其它派生類的基類使用。 它們一般不會直接被實例化。 不同于接口,抽象類可以包含成員的實現(xiàn)細節(jié)。 abstract關(guān)鍵字是用于定義抽象類和在抽象類內(nèi)部定義抽象方法
抽象類中的抽象方法不包含具體實現(xiàn)并且必須在派生類中實現(xiàn)
/**
* @description 抽象類
* @abstract
* @class A
*/
abstract class A{
constructor(public name: string){};
printName(): void{
console.log(`Name is: ${ this.name }.`);
}
abstract printMeeting(): void; // 必須在派生類中實現(xiàn)
}
class B extends A{
constructor(){
super("Lyli");
}
printMeeting(): void{
console.log("meeeting");
}
printHaha(): void{
console.log("heihei..");
}
}
let ah : A;
// ah = new A(); // error, 無法創(chuàng)建抽象類的實例
ah = new B();
ah.printName();
ah.printMeeting();
// ah.printHaha(); // 方法在 A 上不存在
高級技巧
構(gòu)造函數(shù)
當(dāng)你在TypeScript里聲明了一個類的時候,實際上同時聲明了很多東西。 首先就是類的 實例的類型
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter: Greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
這里,我們寫了 let greeter: Greeter,意思是 Greeter類的實例的類型是 Greeter。 這對于用過其它面向?qū)ο笳Z言的程序員來講已經(jīng)是老習(xí)慣了。
我們也創(chuàng)建了一個叫做 構(gòu)造函數(shù)的值。 這個函數(shù)會在我們使用 new創(chuàng)建類實例的時候被調(diào)用。 下面我們來看看,上面的代碼被編譯成JavaScript后是什么樣子的:
let Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
let greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
上面的代碼里, let Greeter將被賦值為構(gòu)造函數(shù)。 當(dāng)我們調(diào)用 new并執(zhí)行了這個函數(shù)后,便會得到一個類的實例。 這個構(gòu)造函數(shù)也包含了類的所有靜態(tài)屬性。 換個角度說,我們可以認為類具有 實例部分與 靜態(tài)部分這兩個部分。
把類當(dāng)接口使用
如上一節(jié)里所講的,類定義會創(chuàng)建兩個東西:類的實例類型和一個構(gòu)造函數(shù)。 因為類可以創(chuàng)建出類型,所以你能夠在允許使用接口的地方使用類
/**
* @description 把類當(dāng)接口使用
* @class Point
*/
class Point{
x: number;
y: number;
}
interface Ponit3d extends Point{
z: number;
}
let point3d: Ponit3d = { x: 1, y: 2, z: 3 };