TypeScript 学习之Class
基本使用
class Greeter {// 属性greeting: string;// 构造函数constructor(message: string) {// 用this 访问类的属性this.greeting = message;}// 方法greet() {return 'Hello, ' + this.greeting;}
}
// 实例化
let greeter = new Greeter('World');
声明了一个Greeter
类,类有三个成员:一个是 greeting
属性,一个构造函数和一个greet
方法。使用new
关键字实例化Greeter
对象。实例化时会调用类的构造函数,并返回一个对象。
继承
使用
extends
关键字。
派生类(子类) 包含构造函数就必须调用super()
class Animal {move(distanceInMeters: number = 0) {console.log(`Animal moved ${distanceInMeters}`);}
}class Dog extends Animal {bark() {console.log('Woof! Woof');}
}const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
最基本的继承:类从基类中继承了属性和方法。
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方法move(distanceInMeters = 5) {console.log('Slithering....');super.move(distanceInMeters);}
}class Horse extends Animal {constructor(name: string) {super(name);}// 重写父类move方法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);
公有、私有与受保护的修饰符
在类中所有成员都默认被公有
public
修饰符修饰。
public
公有- 公有(
public
)成员可能在类的外部访问
- 公有(
class Animal {public name: string;public constructor(theName: string) {this.name = theName;}public move(distanceInMeters: number) {console.log(`${this.name} moved ${distanceInMeters}`);}
}let animal = new Animal('dog');
animal.name = 'Cat';
animal.move(20);
private
私有- 当成员被标记成
private
时,属性或方法就不能在类的外部访问 - 子类不能访问父类的
private
修饰的属性 constructor
可以被private
修饰,但是这样类就不能被实例化,也不能被子类继承
- 当成员被标记成
class Animal {private name: string;constructor(theName: string) {this.name = theName;}
}class Dog extends Animal {constructor(theName: string) {super(theName);}move() {console.log(`${this.name}`); // 报错,子类不能访问父类的私有属性}
}
let animal = new Animal('dog');
animal.name = 'cat'; // 报错,name 是私有属性,类外不能访问
- 如果两个的类型的所有成员的类型都是兼容的,那么这两类型是兼容的。但是这两个类型的其中一个类型包含
private
成员,那么只有当另一个类型中也存在这样一个private
成员,并且来自同一处声明时,才能认为这两个类型是兼容的protected
成员也使用这个规则
class Animal {private name: string;constructor(theName: string) {this.name = theName;}
}class Rhino extends Animal {constructor() {super('Rhino');}
}class Employee {private name: string;constructor(theName: string) {this.name = theName;}
}let animal = new Animal('Goat');
let rhino = new Rhino();
let employee = new Employee('Bob');animal = rhino;
animal = employee; // 错误:Animal 与 Employee 不兼容
protected
保护- 子类可以访问父类的
protected
修饰的成员。但是protected
修饰的成员不能再类的外部访问 constructor
可以被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 修饰的属性不能再类的外部访问
readonly
修饰符
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是只读的
参数属性
参数属性通过给构造函数参数前面添加一个访问限定符来声明。
会同时声明并初始化一个成员
- 使用
readonly
修饰的属性与构造函数的参数结合。
class Octopus {readonly numberOfLegs: number = 8;constructor(readonly name: string) {}
}let dad = new Octopus('Man with the 8 strong legs');
dad.name = 'Man with the 3-piece suit'; // 错误 namename 只读console.log(dad.name);
- 使用
public
修饰的属性与构造函数的参数结合。
class Octopus {readonly numberOfLegs: number = 8;constructor(public name: string) {}
}let dad = new Octopus('man');
console.log(dad.name);
- 使用
private
修饰的属性与构造函数的参数结合。
class Octopus {readonly numberOfLegs: number = 8;constructor(private name: string) {}getName() {return this.name;}
}let dad = new Octopus('man');
console.log(dad.getName());
- 使用
protected
修饰的属性与构造函数的参数结合。
class Octopus {readonly numberOfLegs: number = 8;constructor(protected name: string) {}getName() {return this.name;}
}let dad = new Octopus('man');
console.log(dad.getName());
存储器
typescript 支持通过 getters/setters 来截取对对象成员的访问。
只有get
没有set
的会自动被推断为readonly
。
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);
}
静态属性
关键字:
static
静态属性是存在于类本身,不存在与类的实例。
同一个类的实例对象共享类上的静态属性的状态。
通过类名.
方式访问静态属性
class Grid {static origin = {x: 0, y: 0};constructor(public scale: number) {}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;}
}let grid1 = new Grid(1.0);
let grid2 = new Grid(5.0);console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
抽象类
关键字:
abstract
抽象类不能直接被实例化,只能通过派生类(子类)继承,实例化派生类(子类)。
抽象类类似于接口,但是抽象类可以包含成员的实现细节。
抽象类的抽象方法不包含具体实现并且必须在派生类(子类)中实现。
抽象方法必须在方法前声明abstract
关键字并且可以包含访问修饰符
abstract class Deparment {constructor(public name: string) {}printName(): void {console.log('Department name: ' + this.name);}abstract printMeeting(): void; // 必须在派生类中实现
}class AccountingDepartment extends Deparment {constructor() {super('Accounting and Auditing'); // 在派生类的构造函数必须调用}printMeeting(): void {console.log('The Accounting Department meets each Monday at 10am.');}generateReports(): void {console.log('Generating accounting reports...');}
}let department: Deparment; // 允许创建一个抽象类型的引用
// department = new Deparment(); // 错误 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许创建一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误:方法在声明的抽象类中不存在
构造函数
- 类的实例化会调用类的构造函数
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());
特别注意:创建一个
greeterMaker
的变量,这个变量保存了这个类或者说保存了类构造函数。然后用typeof greeter
,意思是取Greeter
类的类型,而不是实例的类型,也就是构造函数的类型。这个类型包含了类的所有静态成员和构造函数。
类当作接口使用
类定义会创建类的实例类型和一个构造函数
class Point {x: number;y: number;
}
interface Point3d extends Point {z: number;
}let point3d: Point3d = {x: 1, y: 2, z: 3};