TypeScript基础教程 -- 类

传统方法中,我们通过构造函数实现类的概念,通过原型链实现继承。而在 ES6 中,我们终于迎来了 class。TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。

ES6中类的用法

使用 class 定义类。使用 constructor 定义构造函数。通过 new 生成新实例的时候,会自动调用构造函数。

1
2
3
4
5
6
7
8
9
10
11
class Animal{
constructor (name) {
this.name = name;
}
sayHi() {
return `hello,my name is ${this.name}`;
}
}
let a = new Animal('Tom');
console.log(a.sayHi()); // hello,my name is Tom

使用 extends 关键字实现继承,子类中使用 super 来调用父类的构造函数和方法。

1
2
3
4
5
6
7
8
9
10
11
class cat extends Animal {
constructor (name) {
super(name); // 调用父类的constructor(name)
}
sayHi() {
return super.sayHi(); // 调用父类的sayHi()
}
}
let b = new cat('boom');
console.log(b.sayHi()); // hello,my name is boom

使用 getset 对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal {
constructor(name) {
this.name = name;
}
set name(value) {
console.log('setter: ' + value);
}
get name() {
return 'Jack';
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack

通过 static 关键字修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用。

1
2
3
4
5
6
7
8
class Animal {
static isAnimal(a){
return a instanceof Animal;
}
}
let a = new Animal('Tom');
Animal.isAnimal(a); // true
a.isAnimal(a); // Uncaught TypeError: a.isAnimal is not a function

TypeScript中类的用法

public、private和protected

TypeScript可以使用三种访问修饰符,分别是:publicprivateprotected

  • public 修饰的属性或方法是公有的,可以再任何地方被访问。默认所有的属性和方法都是 public 的;
  • private 修饰的属性或方法是私有的,不能再声明它的类的外部访问;
  • protected 修饰的属性或方法是受保护的,和 private 类似,区别是,它在子类中也是允许被访问的。

举例说明:

1
2
3
4
5
6
7
8
9
10
class Animal {
public name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Tom');
console.log(a.name); // Tom
a.name = 'Jack';
console.log(a.name); // Jack

上例中,name 被设置为 public,所以直接访问实例的 name 属性是被允许的。
很多时候,我们希望有的属性是无法直接存取的,这时候就要用到 private 了。

1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
private name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Tom');
console.log(a.name);
a.name = 'Jack';
// hello.ts(8,15): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
// hello.ts(9,3): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
// hello.ts(10,15): error TS2341: Property 'name' is private and only accessible within class 'Animal'.

需要注意的是,TypeScript编译后的代码中,并没有限制 private 属性再外部的可访问性。下面是编译后的代码:

1
2
3
4
5
6
7
8
9
10
var Animal = /** @class */ (function () {
function Animal(name) {
this.name = name;
}
return Animal;
}());
var a = new Animal('Tom');
console.log(a.name); // Tom
a.name = 'Jack';
console.log(a.name); // Jack

另外,使用 private 修饰的属性或方法在子类中是不允许访问的:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {
private name;
public constructor(name) {
this.name = name;
}
}
class cat extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
}
// hello.ts(10,22): error TS2341: Property 'name' is private and only accessible within class 'Animal'.

使用 protected 则允许在子类中访问

1
2
3
4
5
6
7
8
9
10
11
12
class Animal {
protected name;
public constructor(name) {
this.name = name;
}
}
class cat extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
}

抽象类

abstract 用于定义抽象类和其中的抽象方法。

首先,抽象类不允许被实例化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
let a = new Animal('Tom');
// hello.ts(9,9): error TS2511: Cannot create an instance of the abstract class 'Animal'.
```
上面,我们定义了一个抽象类 Animal 和一个抽象方法 sayHi(),在实例化的时候报错了。
其次,抽象类中的抽象方法不许被子类实现:
``` ts
class cat extends Animal {
public eat() {
console.log(`${this.name} is eating.`);
}
}
let cat1 = new cat('Tom');
// hello.ts(9,7): error TS2515: Non-abstract class 'cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.

我们定义了一个类 cat 继承抽象类 Animal,但没有实现抽象方法 sayHi(),所以编译报错。

正确方法:

1
2
3
4
5
6
7
8
class cat extends Animal {
public sayHi() {
console.log(`hello, my name is ${this.name}`);
}
}
let cat1 = new cat('Tom');
cat1.sayHi(); // hello, my name is Tom

类的类型
1
2
3
4
5
6
7
8
9
10
11
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack