跳到主要内容

基本使用

  • TS 中的类和 ES6 中的类大部分相同
  • TS 中的 class 支持类型注解、访问修饰符、抽象类、接口、只读属性、泛型等

例如:

class Foo {
static count: number = 1;
content: string = 'hello world';
say() {
return this.content;
}
}

class Bar extends Foo {
song() {
return 'dadada';
}
}

const msg = new Bar();
console.log(Foo.count); // 1
console.log(msg.say()); // hello world
console.log(msg.song()); // dadada

首先声明了一个 Foo 类,有 content 属性和 say 方法。然后声明了一个 Bar 类继承 Foo 类,创建实例后调用 say 和 song 方法均可行。

类的重写,就是子类可以重新编写父类里边的代码。比如在子类 Bar 中也写一个 say 方法,可以返回其他内容。super关键字可以调用父类中的方法

class Bar extends Foo {
song() {
return 'dadada';
}
say() {
return super.say() + ' 666';
}
}

const msg = new Bar();
console.log(msg.say()); // hello world 666

访问修饰符

类的访问修饰符有publicprivateprotected三种,分别表示公共的、私有的、受保护的。

  • public:在类的内部和外部都可访问,默认类型
  • private:只能在类的内部访问,外部和继承的子类都不能访问
  • protected:能在类的内部和继承的子类中访问,不能在外部访问
class Foo {
private count: number = 8;
protected content: string = 'hello world';
public say() {
return this.content;
}
}

class Bar extends Foo {
song() {
return this.content;
}
}

const msg = new Bar();
msg.say();

类的构造函数

所有属性要先声明再使用,使用访问类型的修饰符声明属性。

// 没有显式地声明类的属性,会报错
class Circle {
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}

// 正确写法,public 关键字可以省略
class Circle {
public x: number;
public y: number;
public radius!: number; // 如果有属性没在constructor中赋值,会报错,可以使用断言 !
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}

在类 Foo 中定义一个未赋初值的 name 属性、一个赋初值的 age 属性,在创建实例时传递参数通过构造器给 name 属性赋值。

class Foo {
name: string;
age: number = 23;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}

以上写法便于理解,但是还有简便写法:

class Foo {
constructor(public name: string, public age: number = 23) {
// 不用再写 this.name = name;
}
}

这种写法相当于定义了 name 和 age 属性,然后在构造器中进行赋值。注意属性前面的public关键字不能少

类的继承

  • 在子类中使用构造器需要使用super()调用父类的构造器,如果有参数还需传递参数。
  • 如果在父类中没有显示的声明构造器,也有默认的构造器constructor() {},仍需调用super()
class Foo {
constructor(public name: string, public age: number) {}
}

class Bar extends Foo {
constructor(public name: string, public age: number, public msg: string) {
super(name, age);
}
}
  • 如果 constructor 被 private 修饰,则该类不允许被继承、不允许外部实例化
  • 如果 constructor 被 protected 修饰,则该类允许被继承、不允许外部实例化
class Foo {
private constructor(public name: string, public age: number) {}
}

// 报错
class Bar extends Foo {
constructor(public name: string, public age: number) {
super(name, age);
}
}
// 报错
const res = new Foo('zgh', 23);
提示

constructor 被 private 或 protected 修饰的场景,可以用于设计单例模式或限制类的实例化

readonly

readonly 表示只读属性。如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。

class Foo {
constructor(public readonly name: string) {}
}
const foo = new Foo('zgh');
console.log(foo.name);
// a.name = 'Tom'; // 报错

类的 getter、setter

假设有一个私有属性_age,外部是不能访问的,可以通过gettersetter属性从外部获取和设置

class girlFriend {
constructor(private _age: number) {}
get age() {
return this._age;
}
set age(age: number) {
this._age = age;
}
}
const girl = new girlFriend(18);
console.log(girl.age); // 18
girl.age = 20;
console.log(girl.age); // 20

抽象类

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

  • 抽象类不允许被实例化
  • 抽象方法没有具体逻辑,不能加大括号
  • 抽象方法必须在子类中实现
  • static 修饰符不能与 abstract 修饰符一起使用
  • 抽象类中可以有方法的具体实现
// 抽象类
abstract class Foo {
static name: string; // 静态方法
// abstract static name: string; // 报错
abstract foo(): void; // 抽象方法
// 方法的具体实现:
bar() {
console.log(1);
}
}

例如:

abstract class Hobby {
abstract skill(): string;
}

class Song extends Hobby {
skill() {
return '唱';
}
}

class Jump extends Hobby {
skill() {
return '跳';
}
}

class Rap extends Hobby {
skill() {
return 'rap';
}
}

在抽象类中定义抽象方法,一般使用形如:abstract foo(): void;这种方式。

下面代码的报错原因是:在类 Foo 中定义了实例成员属性 foo,但子类 Bar 将其定义为实例成员函数。

abstract class Foo {
// abstract foo(): void;
abstract foo: () => void;
}

class Bar extends Foo {
foo() {} // 报错
}

使用 interface 声明抽象类:

interface PersonInterface {
name: string;
age: number;
greet(): void;
}

class Idol implements PersonInterface {
constructor(public name: string, public age: number) {}
greet(): void {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}

const idol = new Idol('ikun', 18);
console.log(idol);
idol.greet();