在最近看一些代码时,发现ts的class可以当做接口(interface)用。为了知道ts的class还有哪些骚操作,我去把官方文档完整地看了一遍。
定义
ts定义类的写法和es6一样:
class Human {
name:string;
constructor (input:string) {
this.name = input;
}
get_name() {
return this.name;
}
}
继承
ts的接口(interface)是可以多继承的,但类(class)和其他语言一样只能单继承。
class Teacher extends Human {
constructor (name:string, subject:string) {
//调用父类的构造器
super(name);
this.subject = subject;
}
subject:string;
get_subject() { // 定义公有方法
return this.subject;
}
}
属性修饰符
ts的属性修饰符除了基础的public,private,protected,static外,还有一个readonly。我们通过一段代码来讲解这几个属性的区别。
class Human {
constructor (name:string = '', age:number = 0, stature:number = 0, nationality:string = '') {
this.nationality = nationality;
this.stature = stature;
this.name = name;
this.age = age;
Human.times += 1; // 静态类型属性,记录构造函数方法被调用的次数
}
public name:string; // 公有属性
private stature:number; // 私有属性
protected age:number; // 保护属性
static times:number = 0; // 静态属性
readonly nationality:string; // 只读属性
get_stature() {
return this.stature;
}
get_times() {
console.log(Human.times);
}
}
class Teacher extends Human {
constructor (name:string, age:number, stature:number, nationality:string, subject:string) {
super(name, age, stature, nationality);
this.subject = subject;
}
subject:string;
set_age(age:number) {
this.age = age;
}
get_subject() { // 定义公有方法
return this.subject;
}
}
const teacher = new Teacher('华', 93, 185, 'US', 'English');
公有属性,readonly属性,静态属性均在类外可直接访问
teacher.name = '莱';
teacher.name;
readonly属性可读不可写
teacher.nationality; // US
teacher.nationality = 'CN'; // 报错
静态属性只属于类,不属于任何实例。用 “类名.属性名” 调用
Human.times; // 构造器被调用的次数,1
Human.times += 1; // 2
私有属性和保护属性均不可在类外直接访问,只能通过类所暴露的公有方法访问和修改
teacher.stature = 160; // 报错
teacher.get_stature(); // 180
teacher.age; // 报错
teacher.set_age(94); // 94
唯一的区别是,私有属性在子类不可访问,保护属性在子类可访问
class Student extends Human {
constructor (grade:number) {
super();
}
get_super_properties() {
console.log(super.age); // 子类可访问父类保护属性
console.log(super.stature); // 报错,子类不可访问父类私有属性
}
}
存取器
类似于拦截器,通过getters/setters来截取对对象成员的访问。只带有 get不带有 set的存取器自动被推断为 readonly。
class Student {
private _name:string;
age:number = 3;
constructor (name:string) {
this._name = name;
}
get name() {
return `Student: ${this._name}`
}
set name(name:string) {
if(this.age < 100) {
throw new Error('too young, too ...');
}
this._name = name;
}
}
const student = new Student('张');
student.name; // Student: 张
student.name = 'haha'; // 抛错
抽象类
抽象类主要作为子类的基类使用,很少用于实例化对象。不同于接口,抽象类可以包含成员的实现细节。(当然也可以只在抽象类做方法签名,在子类做实现方法,但这样和接口无异了)。
abstract class Human {
// 缩写,隐含this.name = name
constructor(public name:string) {
}
// 定义了接口签名,必须在派生类中实现
abstract run() : void;
get_name() {
return this.name;
}
}
class Journalist extends Human {
constructor(public name:string = '张') {
super(name);
}
// 单位km/h
speed:number = 0;
// 实现基类定义的方法
run() {
this.speed = 6;
console.log('fast')
}
}
const journalist = new Journalist();
journalist.run();