TypeScript 学习总结之 类


TypeScript

类的注解方式

1
2
3
4
5
6
7
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}

继承中super

// 注意这个super, 如果在constructor内就是默认指 构造函数 。

// 如果在constructor外部(除了constructor),这个super 就是指的 父类 (Animal)。

1
2
3
4
5
6
7
class Snake extends Animal {
constructor(name: string) { super(name); } // 这里的super => constructor Animal(theName: string) : Animal
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters); // 这里的 super => 父类 Animal
}
}

类的修饰符

三大类成员修饰符: public / private / protected

public

// 在TypeScript里,成员都默认为 public。

// 如果类默认不写, 就是public

public : 公共成员属性

  • 自身类可以调用
  • 子类可以调用
  • 实例可以调用
1
2
3
4
5
6
7
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 : 私有属性

  • 只能自身调用

// 当我把 Animal 类中的 构造函数变成私有, 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
public name: string;
private constructor(theName: string) { this.name = theName; }
public 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);
}
}

let sam = new Snake("Sammy the Python");
sam.move();

当我把 构造函数变成私有属性时, 编辑器会提示说 我子类(Snake)继承不了
private

// 当我把 Animal 中的 move 方法设为 private 时, 会出现如下报错:
private

因此 , 可证明 private 只能自身调用, 子类和实例不能调用.

protected

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。

也就是说 protected 可以自身调用,也可以子类调用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
protected 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);
}
}

let jack = new Animal('jack')
jack.move() // 报错,

这时,在编辑器上, 实例调用move方法会报错,
protected

readonly(只读)修饰符

1
2
3
4
5
6
7
8
9
10
11
12
class Octopus {
public readonly name: string; // readonly 要放在 public 这种访问属性修饰符 后面
private readonly numberOfLegs: number = 8;
constructor(theName: string) {
this.name = theName;
}
readonly say() { // 错误 : 修饰符仅可出现在属性声明或索引签名中
console.log('hi');
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

readonly 注意事项:

  • 顺序: 要放在 public/private/protected 之后
  • 是否可写: 加上 readonly 表示 只读,不可写
  • 不能修饰成员方法.

readonly

参数属性

简写写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal1 {
constructor(private name: string) { }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}

// 等价于

class Animal2 {
private name: string
constructor(name: string) { this.name = name }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}

存取器

大概看了一下, 其实就是 getter 和 setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let passcode = "secret passcode";

class Employee {
private _fullName: string = 'jack';

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();
console.log(employee.fullName); // jack

console.log(employee.fullName = 'new jack'); // new jack

静态属性

这个也是 不属于 TS 新有的知识, 学到这里,我发现其实我们大都是还是在复习JS, 要学好TS前提是JS基础一定要好啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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}));

抽象类

抽象类: 能够提供其他类的基类 , 它们一般不会直接被实例化

抽象类:

  1. 无法创建实例
  2. 在抽象类中 抽象方法一定要实现 (这一点和接口很像)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('我在移动');
}
}

class Dog extends Animal {
makeSound() { // 一定要去定义这个方法, 因为抽象类中包含这个抽象方法
console.log('汪汪汪');
}
}

class Cat extends Animal {
makeSound() {
console.log('喵喵喵');

}
}

高阶技巧

  1. 定义类的时候, 定义了一个类型;
  2. 定义类的时候, 定义了一个构造函数;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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());
  1. 把类当做接口使用
1
2
3
4
5
6
7
8
9
10
class Point {
x: number;
y: number;
}

interface Point3d extends Point {
z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};