- TypeScript類的基本使用(修飾符)
- TypeScript類的抽象類(abstract)
- TypeScript類的高級技巧
一、TypeScript類的基本使用(修飾符)
TypeScript的類與ES6的類非常類是,所以建議先了解ES6的類的相關內容:ES6入門六:class的基本語法、繼承、私有與靜態屬性、修飾器。
1.最簡單的TypeScript的類示例與ES6實現對比:
1 class tsClass{ 2 public a:string ; //公有成員 3 private b:number[] ; //私有成員 4 protected c:string[] ; //被保護的成員 5 static e:string = 'e'; //靜態屬性 6 constructor(astr:string,barr:number[],carr:string[]){ 7 this.a = astr; 8 this.b = barr; 9 this.c = carr; 10 } 11 } 12 class SublevelCla extends tsClass { 13 private dname: string; //私有成員 14 constructor (astr:string,barr:number[],carr:string[],dname:string){ 15 super(astr,barr,carr);//繼承tsClass構造字段 16 this.dname = dname;//sublevelCla自身構造字段 17 } 18 fun():void{ 19 console.log(tsClass.e);//通過類獲取靜態成員 20 } 21 } 22 23 let sub = new SublevelCla('a',[1,2,3],['a','b','c'],'sublevelName'); 24 sub.fun(); 25 console.log(sub.a); 26 // console.log(sub.b); //報錯:私有成員不能被外部訪問 27 // console.log(sub.c); //報錯:被保護的成員不能被外部訪問 28 // console.log(sub.dname); //報錯:私有成員不能被外部訪問
Ts類與Js類的修飾符對比:
Ts有公共成員(public)修飾符;Js沒有該修飾符,但可以在構造函數constructor內直接使用this定義公共成員,用於生成每個實例對象的屬性。
Ts有私有成員(private)修飾符;Js的私有成員修飾符是(#),該成員只能在當前類中使用,TS與JS沒有區別。
Ts有受保護成員(protected)修飾符;Js沒有該修飾符,也沒有對應的成員語法。在Ts中受保護成員可以被字類訪問在子類中使用,但不能被實例對象在外部訪問。
Ts有靜態成員(static)修飾符;Js中也有同樣的修飾符,該成員最終被解析到類的自身屬性上(解析成ES5的話就是函數的屬性),靜態屬性可以被類名直接在任何地方引用。
Ts有只讀成員(readonly)修飾符;Js中沒有該修飾符,但是可以通過屬性訪問器get來實現。
1 //js示例代碼 2 class jsClass{ 3 #b ; 4 #c ; 5 static e = 'e'; 6 constructor(astr,barr,carr,){ 7 this.a = astr; 8 this.#b = barr; 9 this.#c = carr; 10 } 11 } 12 13 class jsSubc extends jsClass{ 14 constructor(astr,barr,carr,dname){ 15 super(astr,barr,carr); 16 this.dname = dname; 17 } 18 fun(){ 19 console.log(jsClass.e); 20 } 21 } 22 let jsub = new jsSubc('a',[1,2,3],['a','b','c'],'sublevelName'); 23 jsub.fun(); 24 console.log(jsub.a); 25 // console.log(jsub.b);//undefined
關於類的繼承在Ts和Js中都是使用extends關鍵字,並且在構造函數中使用super方法實現構造繼承,這個方法都必須寫在字類構造函數內的最前面。
Ts受保護的成員的使用方式就是通過添加到父類構造函數,然后字類構造繼承該成員,在子類中就能使用this關鍵字訪問該成員了,子類中不需要再在自生聲明該成員了。
1 //這個官方示例完美的展示了受保護成員的應用 2 class Person { 3 protected name: string; 4 constructor(name: string) { this.name = name; } 5 } 6 7 class Employee extends Person { 8 private department: string; 9 10 constructor(name: string, department: string) { 11 super(name) 12 this.department = department; 13 } 14 15 public getElevatorPitch() { 16 return `Hello, my name is ${this.name} and I work in ${this.department}.`; 17 } 18 } 19 20 let howard = new Employee("Howard", "Sales"); 21 console.log(howard.getElevatorPitch()); 22 console.log(howard.name); // 錯誤
Ts只讀成員示例在官方文檔中也有非常好的示例,這里直接復制展示該示例:
1 class Octopus { 2 readonly name: string; 3 readonly numberOfLegs: number = 8; 4 constructor (theName: string) { 5 this.name = theName; 6 } 7 } 8 let dad = new Octopus("Man with the 8 strong legs"); 9 dad.name = "Man with the 3-piece suit"; // 錯誤! name 是只讀的.
同樣Ts中也可以使用set與get作為存取器,通過屬性訪問器setter和getter實現的成員相當於定義了一個(public)公有成員,每個實例都會產生自己的實例屬性,唯一的區別就是在不寫入值時會讀取類中定義的私有屬性值,這個特性與JS完全一致。常見的使用訪問器實現讀寫私有屬性(一般只寫):
1 class Person{ 2 private _name: string = 'person'; 3 //給私有屬性賦值 4 set setName(val:string){ 5 this._name = val; 6 } 7 //讀取私有屬性值(一般不建議使用) 8 get getName(){ 9 return this._name; 10 } 11 } 12 var obj1 = new Person(); 13 var obj2 = new Person(); 14 obj1.setName = 'obj1'; 15 console.log(obj2.getName);//person 16 console.log(obj1.getName);//obj1

1 //js 2 class Person{ 3 #name = 'person'; 4 //給私有屬性賦值 5 set setName(val){ 6 this.#name = val; 7 } 8 //讀取私有屬性值(一般不建議使用) 9 get getName(){ 10 return this.#name; 11 } 12 } 13 var obj1 = new Person(); 14 var obj2 = new Person(); 15 obj1.setName = 'obj1'; 16 console.log(obj2.getName);//person 17 console.log(obj1.getName);//obj1
二、TypeScript類的抽象類(abstract)
關於抽象類需要先理解幾個概念,什么是抽象類,什么是派生類,什么是抽象類型的引用。
抽象類顧名思義就是類的抽象,它不是用來具體實現對象實例化的類,而是定義類結構。比如規定類必須要實現那些方法和成員變量,但其自身並不做具體細節實現,這些方法也叫做抽象方法和抽象成員變量(同樣使用abstract關鍵字修飾),這些被抽象出來的方法和成員變量必須在派生類中實現。抽象類自身也可以包含成員的實現細節,比如定義構造函數的具體內容,成員變量和方法,這些實現細節會被派生類繼承用於實例化具體的對象。
派生類就是抽象類的具體實現,在派生類中必須實現抽象類中定義的抽象方法和成員變量,並且不能隨意增加抽象類沒有定義的方法和成員變量(即使能在抽象類中實現,但是不能被抽象類的引用使用這些在派生類中私自定義的內容,但可以被非抽象類引用的變量接收),這一點與繼承是有區別的。並且在構造函數constructor內必須使用super()方法繼承抽象類的構造。
抽象類型的引用就是變量類型為抽象類的變量,該變量只能引用抽象類對應的派生類構造的對象,並且只能使用抽象類自身實現的細節內容和抽象內容,而不能使用派生類中實現的非抽象內容。
1 abstract class Department { 2 constructor(public name: string) { 3 } 4 printName(): void { 5 console.log('Department name: ' + this.name); 6 } 7 abstract printMeeting(): void; // 必須在派生類中實現 8 } 9 10 class AccountingDepartment extends Department { 11 public a:string; 12 constructor() { 13 super('Accounting and Auditing'); // 在派生類的構造函數中必須調用 super() 14 this.a='aaa' 15 } 16 printMeeting(): void { 17 console.log('The Accounting Department meets each Monday at 10am.'); 18 } 19 generateReports(): void { 20 console.log('Generating accounting reports...'); 21 } 22 } 23 24 25 26 let department: Department; // 允許創建一個對抽象類型的引用 27 // department = new Department(); // 錯誤: 不能創建一個抽象類的實例 28 department = new AccountingDepartment(); // 允許對一個抽象子類進行實例化和賦值 29 department.printName(); 30 department.printMeeting(); 31 // department.generateReports(); // 錯誤: 方法在聲明的抽象類中不存在 32 33 let accounting = new AccountingDepartment();//非抽象引用變量能接收的派生類實例對象 34 accounting.generateReports();//並且能使用派生類自身定義的內容
三、TypeScript類的高級技巧
1.構造函數
關於構造函數作為類的核心內容,是每個類實例化對象時new指令直接調用的方法,實際上TypeScript與ES6在這方面並沒有差別,如果了解ES6的化我們都知道class只是一個語法糖,它的底層實現還是function,類中的構造函數就是ES5的方法主體,它最終被賦給該方法原型上的constructor屬性,而類中實現的一系列的修飾符和特性最后都會被解析為類自身或者原型上的屬性和方法,用來配合主體方法實現對象實例的構造。
1 //ES5基於function實現的類 2 let Greeter = (function () { 3 function Greeter(message) { 4 this.greeting = message; 5 } 6 Greeter.prototype.greet = function () { 7 return "Hello, " + this.greeting; 8 }; 9 return Greeter; 10 })(); 11 12 let greeter; 13 greeter = new Greeter("world"); 14 console.log(greeter.greet());
再來對比看一下TypeScript的改寫實現:
1 class Greeter { 2 static standardGreeting = "Hello, there"; 3 greeting: string; 4 greet() { 5 if (this.greeting) { 6 return "Hello, " + this.greeting; 7 } 8 else { 9 return Greeter.standardGreeting; 10 } 11 } 12 } 13 14 let greeter1: Greeter; 15 greeter1 = new Greeter(); 16 console.log(greeter1.greet()); 17 18 let greeterMaker: typeof Greeter = Greeter; 19 greeterMaker.standardGreeting = "Hey there!"; 20 21 let greeter2: Greeter = new greeterMaker(); 22 console.log(greeter2.greet());
2.把類當成接口(關於接口在TypeScript接口相關博客中會有更詳細的內容)
1 class Point { 2 x: number; 3 y: number; 4 } 5 6 interface Point3d extends Point { 7 z: number; 8 } 9 10 let point3d: Point3d = {x: 1, y: 2, z: 3};