类的继承
类的继承是使用 extends:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
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);
执行结果:1
2
3
4Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.
以上的例子是 Snake 和 Horse 继承自 Animal,而 Snake 和 Horse 就称为 派生类 / 子类,而 Animal 就被称为 基类 / 超类。
如果派生类内包含了构造函数,在构造函数中必须访问 super()
,它会执行基类的构造函数。尤其是在调用 this
之前,使用super
进行重写了基类的 move 方法。
Public, private, and protected modifiers
public 默认值
1 | class Animal { |
private
当成员被标记成 private时,它就不能在声明它的类的外部访问。
1 | class Animal { |
protected
- protected 和 private 很相似,唯一不同的地方是 protected 成员在派生类中是可以被访问的。
1 | class Person { |
- 构造函数也可以被标记为 protected,这样的话这个类只能被继承,不能被包含他的类外面实例化。
对于 private 和 protected 申明的成员,如果要比较两个类型是否一样时就会出现不同。
例如:
1 | class Animal { |
因为 Employee 和 Animal 的 name 不是同一个 name
readonly 修饰符
只读属性必须在声明时或构造函数里被初始化。
1 | class Octopus { |
参数属性
1 | /** |
在构造函数里使用 private name: string
参数来创建和初始化 name
成员,把声明和赋值合并至一处
存取器
使用 get
和 set
设置成为一个存取器
1 | /* |
- 存取器要求你将编译器设置为输出 ECMAScript 5 或更高。 不支持降级到 ECMAScript 3。
- 只带有 get 不带有 set 的存取器自动被推断为 readonly。 这在从代码生成 .d.ts文件时是有帮助的,因为利用这个属性的用户会看到不允许够改变它的值。
静态属性
通过 static 关键字来设置,上面说的时只有在实例化之后才会初始化的值,静态属性是在类本身里面,它也可以通过 this.
来进行方法问。
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 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}));
抽象类
使用 abstract 声明。1
2
3
4
5
6
7
8
9/*
抽象类内部定义的抽象方法
*/
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
1 | abstract class Department { |
注意点:
- 允许创建一个抽象对象类型
- 抽象类不允许实例化
- 允许对抽象类的子类进行实例化和赋值
- 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。定义方法签名但不包含方法体。
- 抽象方法必须包含 abstract关键字并且可以包含访问修饰符。
高级技巧
- 构造函数
当你在 ts 中声明了一个类时,就同时声明了很多东西,
例如:
- 类的实例的 类型
- 构造函数,这个函数在这个类被 new 的时候调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28class 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());
- 把类当做接口使用
1
2
3
4
5
6
7
8
9
10class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};