TypeScript入门四:TypeScript的类(class)

  • 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
js示例

 二、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};

 

posted @ 2019-11-05 02:37  他乡踏雪  阅读(11338)  评论(0编辑  收藏  举报