类
基本使用
- 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
访问修饰符
类的访问修饰符有public
、private
、protected
三种,分别表示公共的、私有的、受保护的。
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
,外部是不能访问的,可以通过getter
和setter
属性从外部获取和设置
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();