类
下面请看类的使用
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; }}let greeter = new Greeter("world");
有三个部分一个叫做 greeting
的属性,一个构造函数和一个 greet
方法。
继承
class Animal { name:string constructor(theName:string){ this.name = theName } move(distanceInMeters:number = 0){ console.log(`${this.name} moved ${distanceInMeters}.`) }}class Snake extends Animal { constructor(name:string){ super(name) } move(distanceInMeters = 5){ console.log("Slithering...") super.move(distanceInMeters) //调用父类的move方法 }}class Horse extends Animal { constructor(name:string){ super(name) } move(distanceInMeters = 45){ console.log("Galloping...") super.move(distanceInMeters) }}let sam = New Snake("Sammy the Python")let tom:Animal = new Horse("Tommy the Palomino")sam.move()tom.move(34)
派生类包含了一个构造函数,它必须调用 super()
,它会执行基类的构造函数。
如果在要构造器里访问this属性,必须先调用super()
同时,这个例子还重写了move
方法
//打印结果Slithering...Sammy the Python moved 5m.Galloping...Tommy the Palomino moved 34m.
公共,私有与受保护的修饰符
public
在TypeScript里,成员都默认为 public
。
也可以这样写出来
class Animal { public name: string; public constructor(theName: string) { this.name = theName; } public move(distanceInMeters: number) { console.log(`${this.name} moved ${distanceInMeters}m.`); }}
private
当成员被标记成 private
时,它就不能在声明它的类的外部访问
class Animal { private name: string; constructor(theName: string) { this.name = theName; }} new Animal("Cat").name; // 错误: 'name' 是私有的.
protected
protected
修饰符与 private
修饰符的行为很相似,但是protected
成员在派生类中仍然可以访问。
class Person { protected name: string; constructor(name: string) { this.name = name; }}class Employee extends Person { private department: string; constructor(name: string, department: string) { super(name) this.department = department; } public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`; }}let howard = new Employee("Howard", "Sales");console.log(howard.getElevatorPitch());console.log(howard.name); // 错误
构造器也能标记成protected
。那么这个类可以被继承,但不能被实例化。
class Person { protected name: string; protected constructor(theName: string) { this.name = theName; }}// Employee 能够继承 Personclass Employee extends Person { private department: string; constructor(name: string, department: string) { super(name); this.department = department; } public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`; }}let howard = new Employee("Howard", "Sales");let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.
readonly修饰符
class Octopus { readonly name: string; readonly numberOfLegs: number = 8; constructor (theName: string) { this.name = theName; }}let dad = new Octopus("Man with the 8 strong legs");dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.
参数属性
将上一段运用参数属性来修改。如下:
class Octopus { readonly numberOfLegs: number = 8; constructor(readonly name: string) { }}
参数属性通过给构造函数参数前面添加一个访问限定符来声明。 使用 private
限定一个参数属性会声明并初始化一个私有成员;对于 public
和 protected
来说也是一样。
存取器
无存取器的代码
class Employee { fullName: string;}let employee = new Employee();employee.fullName = "Bob Smith";if (employee.fullName) { console.log(employee.fullName);}
set
方法与get
方法
let passcode = "secret passcode";class Employee { private _fullName: string; 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 employee!"); } }}let employee = new Employee();employee.fullName = "Bob Smith";if (employee.fullName) { alert(employee.fullName);}
注意只带有 get
不带有 set
的存取器自动被推断为 readonly
。
静态属性
静态属性只存在类身上,而不在类的实例上。
class Grid { static origin = { x: 0, y: 0 }; calculateDistanceFromOrigin(point: { x: number; y: number }) { let xDist = point.x - Grid.origin.x; let yDist = point.y - Grid.origin.y; return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale; } constructor(public scale: number) {}}let grid1 = new Grid(1.0); // 1x scalelet grid2 = new Grid(5.0); // 5x scaleconsole.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 }));console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 10 }));
使用 static
定义 origin
,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 origin
前面加上类名。即Grid.
来访问静态属性。
抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。
abstract class Animal { abstract makeSound(): void; move(): void { console.log("roaming the earch..."); }}
抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 abstract
关键字并且可以包含访问修饰符。
abstract class Department { constructor(public name: string) {} printName(): void { console.log("Department name: " + this.name); } abstract printMeeting(): void; // 必须在派生类中实现}class AccountingDepartment extends Department { constructor() { super("Accounting and Auditing"); // 在派生类的构造函数中必须调用 super() } printMeeting(): void { console.log("The Accounting Department meets each Monday at 10am."); } generateReports(): void { console.log("Generating accounting reports..."); }}let department: Department; // 允许创建一个对抽象类型的引用department = new Department(); // 错误: 不能创建一个抽象类的实例department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值department.printName();department.printMeeting();department.generateReports(); // 错误: 方法在声明的抽象类中不存在
高级技巧
构造函数
// ts写法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());// 上一段 转为 JS写法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());
我们可以认为类具有实例部分与 静态部分这两个部分。
让我们稍微改写一下这个例子,看看它们之间的区别:
class Greeter { static standardGreeting = 'Hello, there'; greeting: string; greet() { if (this.greeting) { return 'Hello, ' + this.greeting; } else { return Greeter.standardGreeting; } }}let greeter1: Greeter;greeter1 = new Greeter();console.log(greeter1.greet());let greeterMaker: typeof Greeter = Greeter;greeterMaker.standardGreeting = 'Hey there!';let greeter2: Greeter = new greeterMaker();console.log(greeter2.greet());
把类当做接口使用
class Point { x: number; y: number;}interface Point3d extends Point { z: number;}let point3d: Point3d = {x: 1, y: 2, z: 3};