一、ES6中的类
参考
js红宝书笔记九 第八章 类与面向对象编程
虽然 ECMAScript 6 类表面上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念。
二、TS类与ES6类的差异
参考
ES6与typescript中的类(class)
1.默认public/privated/protected
私有属性方面ECMAScript目前还没有定案,typescript通过添加private来标记私有属性。
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee 能够继承 Person
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");
let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.
2.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 是只读的.
3.getter/setter一致
4.静态属性
ECMAScript目前没有静态属性
在这个例子里,我们使用 static定义 origin,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 origin前面加上类名。 如同在实例属性上使用 this.前缀来访问属性一样,这里我们使用 Grid.来访问静态属性。
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 scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
5.抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
ECMAScript目前没有抽象类。
抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 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(); // 错误: 方法在声明的抽象类中不存在
三、实例
参考《TypeScript编程》第五章,使用了type关键字作为类型别名,后续会详解。
type Color = 'Black' | 'White';
type File1 = 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H';
type Rank = 1|2|3|4|5|6|7|8;
class Position{
constructor(
private file:File1,
private rank:Rank
){}
distanceFrom(position:Position){
return{
rank:Math.abs(position.rank - this.rank),
file:Math.abs(position.file.charCodeAt(0)-this.file.charCodeAt(0))
};
}
test(){
let v = this.distanceFrom(new Position('A',2));
console.log(v.file,typeof v);
}
}
abstract class Piece{
protected position:Position;
constructor(
//初始赋值后,color只能读取
private readonly color:Color,
file:File1,
rank:Rank
){
this.position = new Position(file,rank);
}
//默认实现,子类可以覆盖
moveTo(position:Position){
this.position = position;
}
//子类必须实现,实现抽象类时也要实现抽象方法
abstract canMoveTo(position:Position):boolean;
}
class King extends Piece{
//这个必须实现,否则报错
canMoveTo(position:Position){
let distance = this.position.distanceFrom(position);
return distance.rank < 2 && distance.file < 2;
}
}
class Game{
private pieces = Game.makePieces();
private static makePieces(){
return [
new King('White','E',1),
new King('Black','E',8),
//...
];
}
}
let t = new Position('C',2);
t.test();
四、super关键字
与JS一样,TS也支持super调用。如果子类覆盖父类中定义的方法,比如Queen和Piece都实现了take方法,在子类中可以使用super调用父类中的同名方法(例如super.take)。
另一种是super(),必须在构造方法中调用,把父子关系连接起来。
注意,super只能访问父类的方法,不能访问父类的属性。