基本写法
如果学习过其他语言或者对ES6有学习过,那么TS中的类就非常好理解。 这里先简单列举一个类的写法:
class Person {
name : string;
// 私有属性
#age : number;
constructor(name: string,age : number) {
this.name = name;
this.#age = age;
}
getName():string{
return this.name;
}
setName (name: string):void{
this.name = name;
}
run(): string {
console.log(`${this.name}今年${this.#age}现在在运动`);
return `${this.name}今年${this.#age}现在在运动`
}
make():void{
console.log("父类在制作");
}
}
let p = new Person("老张",99)
p.run()
console.log(p.getName());
p.setName("小王")
p.run()
在这上述代码中,定义了公有属性name,是string类型,私有属性age,是number类型。构造函数是类里面应该具有的。getName函数返回name属性,返回值是string类型的。setName函数是修改name属性的,无返回值函数。run()函数返回值是string类型的,其功能是对name和age进行输出。这个make函数是验证父类与子类在具有同样函数的情况下到底执行哪一个。
继承
类从基类中继承了属性和方法。 这里, PersonEx 是一个 派生类,它派生自 Person 基类,通过 extends关键字。派生类通常被称作子类,基类通常被称作超类。派生类包含了一个构造函数,它必须调用super(),会执行基类的构造函数。而且,在构造函数里访问 this的属性之前,我们一定要调用 super()。这个是TypeScript强制执行的一条重要规则。
class PersonEx extends Person{
constructor(name: string,age: number) {
super(name,age);
}
work():void{
console.log(`${this.name}在工作!`);
}
make():void{
console.log("子类making");
}
}
let pe = new PersonEx("王晓晨",88)
pe.run();
pe.work();
pe.make();
自己拓展的函数是可以正常调用的,对于这个make函数在前面有说明在父类和子类都有同一个函数的情况下,子类有就先从子类找,子类没有再从父类找。
类里面的修饰符
typescript里面定义属性的时候给我们提供了三种修饰符:
public :公有 在类里面、子类、类外面都可以访问
protected:保护类型 在类里面、子类里面可以访问﹐在类外部没法访问
private :私有 在类里面可以访间,子类、类外部都没法访间
属性如果不加修饰符默认就是公有(public)
除了三种修饰符外,从官方文档看到有readonly修饰符,这个readonly关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。下面我们对这几种修饰符进行验证一下:
class PersonRole{
public name : string;
protected age : number;
private sex : string;
constructor(name: string , age: number, sex: string) {
this.name = name;
this.age = age;
this.sex = sex;
}
run():void{
console.log(`${this.name}在跑步!年龄是:${this.age},性别是${this.sex}`);
}
}
class PersonRoleEx extends PersonRole{
readonly readNum: number = 8;
readonly readPlace: string;
constructor(name: string , age: number, sex: string, readPlace:string) {
super(name,age,sex)
this.readPlace = readPlace;
}
work():void{
console.log(`${this.name}在工作,年龄是${this.age},工作地点是${this.readPlace}!`);
}
make():void{
// console.log(`性别:${this.sex}`);
}
change(readPlace:string):void{
// this.readPlace = readPlace
}
}
let pre = new PersonRoleEx("小吴",25,"男","武汉")
pre.work() //在类里面、子类里面可以访问
// console.log(pre.age); //报错
PersonRole是父类,PersonRoleEx是子类。父类里面有三种不同属性分别用三种不同的修饰符进行修饰,readonly修饰符我们在子类进行验证。我们依次来看,
private子类、类外部都没法访间,因此语法会报错。体现为子类中的make函数如下报错:
protected在类外部没法访问,体现为此处代码段最后一行打印age属性报错
这里因为只读属性不能被修改,所以change函数也会报错。
存取器
官网在这里有对存取器详细的讲解:存取器。 简单来说就是TypeScript支持通过getters/setters来截取对对象成员的访问。它能帮助你有效的控制对对象成员的访问。
在官网中提供了一段代码,但是我按照他提供的代码无法运行的,需要稍作修改(当然这是小问题),报错提示:属性_fullName没有初始值设定项,也没有在构造函数中明确赋值。
对于这个问题,进行修改:
private _fullName: string = " ";
学习这里的存取器时,我觉得它的案例不太好(也可能是我水平不太好),没有很清晰的实现他说的功能:修改一下密码,来验证一下存取器是否是工作的;当密码不对时,会提示我们没有权限去修改员工。对此我将代码修改了一下,代码如下:
class Employee {
input:string = " "
private _fullName: string = "123456";
constructor(input: string){
this.input = input
}
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (this.input && this.input == this._fullName) {
this._fullName = newName;
console.log(`修改成功,现在密码是:${this._fullName}`);
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee("123");
employee.fullName = "Bob Smith";
这些代码首先定义了输入密码、存储密码,这里的get fullName()函数是获取存储密码,set fullName()函数是对用户输入密码进行校验,如果输入密码和存储密码一致就可以进行修改并提示修改成功,否则提示报错信息。这里通过实例化对象的时候传入输入密码,如果和存储密码一致则执行修改。
let employee = new Employee("123");
let employee = new Employee("123456");
多态
父类定义一个方法不去实现,让继承它的子类去实现每一个子类有不同的表现,多态属于继承。
class Animal{
name: string;
constructor(name: string){
this.name = name
}
eat(){
console.log("吃的方法");
}
}
class Dog extends Animal{
constructor(name : string){
super(name)
}
eat() {
return this.name + '吃肉'
}
}
class Cat extends Animal{
constructor(name : string){
super(name)
}
eat() {
return this.name + '吃鱼'
}
}
let dogMul = new Dog("小狗")
console.log(dogMul.eat());
let catMul = new Cat("小猫")
console.log(catMul.eat());
我们来看看这些代码,我们定义了父类 Animal ,Dog 子类和Cat 子类都是继承这个 Animal 父类,但是里面具体的eat函数每一个都不一样。下面是他们的运行结果。
静态方法与静态属性
这里TS的静态方法我感觉和ES6的静态方法类似,之前在JS类研究过,具体在JS中的类中 类里面的静态方法 这一小节中。如果还是有疑问可以看看官方文档 静态属性,这里对静态方法与静态属性也有比较详细的说明。
抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。abstract抽象方法只能放在抽象类里面。抽象类和抽象方法用来定义标准,例如标准: Phone 这个类要求它的子类必须包含 buy 方法。
abstract class Phone {
name: string;
constructor(name: string){
this.name = name
}
abstract buy() : any;
}
class Xiaomi extends Phone {
constructor(name: string){
super(name)
}
buy() {
console.log(`我们卖${this.name}`);
}
}
let xiaomiPhone = new Xiaomi("小米手机")
xiaomiPhone.buy()
如果我们不按要求,不写buy()函数则会如下报错: