typescript基础篇(5):类

时间:2022-07-23
本文章向大家介绍typescript基础篇(5):类,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

5.类

5.1 类的声明与继承

ts的类基本包括了es6+中类的全部功能。假如我要声明一条“狗”类,在ts中实现一个类是这样的:

class Dog {
  constructor(name: string) {
    this.name = name
  }
  name: string
  run() {
    console.log(`${this.name} is running`)
  }
}

const dog = new Dog("Bobby")

类属性都是实例属性,而不是原型属性。ts中属性必须被初始化

如果为狗类声明一个子类(哈士奇类),并且有哈士奇有它的属性:颜色:

class Hasky extends Dog {
  constructor(name: string, color: string) {
    super(name)
    this.color = color
  }

  color: string
}

const hasky = new Hasky("haha", "white")
hasky.run() // haha is running
console.log(hasky.color) // white

5.2 修饰符

5.2.1 public

类所有默认的修饰符都是public。表示作用域内所有成员可见。如:

class Dog {
  constructor(name: string) {
    this.name = name
  }
  public name: string // 类型注解
  run() {
    console.log(`${this.name} is running`)
  }
}

可以不写。在构造函数的参数上使用public等同于创建了同名的成员变量。

5.2.2 private

表示为一个类的私有成员,只有类本身内部可以调用,子类和实例无法调用:

class Dog {
  constructor(name: string) {
    this.name = name
  }
  public name: string // 类型注解

  // 私有成员
  private eat() {
    console.log(`dog is eating`)
  }
  run() {
    console.log(`${this.name} is running`)
  }
}

如果private修饰constructor,表示这个类既不能被实例化,也不能被继承。

5.2.3 protected

Protected修饰需要保护的成员——只能在类和子类中访问,但不能在它们的实例中访问

如果protected修饰的contructor,那么表示它不能被实例化,而只能被继承。也就是所谓的“基类”。

5.2.4 readonly

只读属性。不可以被修改,必须在属性中被实例化。

5.2.5 修饰参数

修饰符也可以用于修饰参数。比如我在参数重直接public color,就不必再申明了,构造函数的写法都可以不写了。

class Hasky extends Dog {
  constructor(name: string, public color: string) {
    super(name)
  }
}

5.2.6 static

静态成员只能通过类名来调用,可以被继承。

class Dog {
  constructor(name: string) {
    this.name = name
  }
  public name: string // 类型注解
  private eat() {
    console.log(`dog is eating`)
  }
  protected sleep() {}
  static food: string = "aaa"
  run() {
    console.log(`${this.name} is running`)
  }
}

5.3 抽象类与多态

ts的抽象类是对js的又一扩展。如果做这么一个类比:有一个“动物”类,它抽象概括了大部分生物的特征,但它没法被实例化,只有通过”狗“类,“人”类等子类来使之形象化。

因而,可以说抽象类是只能被继承但不能实例化的类。在ts中,通过abstract来声明抽象类。

abstract class Animal {
  constructor() {}

  shout() {
    console.log("shout")
  }
}

class Dog extends Animal {
  constructor(public name: string) {
    super()
  }
  run() {
    console.log(`${this.name} is running`)
  }
}

const dog = new Dog("Bobby")
dog.shout()

抽象类中,也可以不指定方法等具体实现,这就构成了抽象方法。子类有此实现,就没必要在在父类中实现了。

abstract class Animal {
  constructor() {}

  abstract shout(): void
}

class Cat extends Animal {
  constructor() {
    super()
  }
  shout() {
    console.log("miao")
  }
}

class Dog extends Animal {
  constructor() {
    super()
  }

  shout() {
    console.log("wang")
  }
}

const animals: Animal[] = [new Cat(), new Dog()]
animals.forEach((animal) => {
  animal.shout()
})// miao wang

上面的例子中,给Animal定义了一个shout方法。但是具体的实现交给了子类来完成。从这个意义来说,模板方法模式的思想很好地得到了实现。

不单类可以多态,this也可以多态。我想在ts中实现类似jquery的链式调用。和js写法一致,思路依然是在原型方法中return this

class WorkFlow {
  step1() {
    return this
  }

  step2() {
    return this
  }
}

new WorkFlow().step1().step2()

如果在子类中这么做,this可以同时调用子类和父类的方法:

class MyFlow extends WorkFlow {
  next() {
    return this
  }
}

new MyFlow().step1().next().step2()

5.4 类与接口的关系

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。这个特性大大提高了面向对象的灵活性。

举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它。

通过接口,可以约束类成员有哪些属性,还有哪些类型。比如说,我先定义一个“人”类,包括name属性(string),和eat方法(void)。然后通过implements关键字,按照这个接口去实现“亚洲人”类。

interface Human {
  name: string
  eat(): void
}

class Asian implements Human {
  constructor(public name: string) {}
  eat() {}
}

注意,类必须要完整声明完接口声明的成员(公有成员,不可改动)。如果类有自己的属性,这是可以的。也不能在接口定义构造函数。

5.4.1 接口之间的继承

interface Human {
  name: string
  eat(): void
}

class Asian implements Human {
  constructor(public name: string) {}
  eat() {}
}

interface American extends Human {
  run(): void
}

interface European extends Human {
  cry(): void
}

interface African extends American, European {}

// 必须完整定义完父接口的方法
const p: African = {
  name: "",
  run() {},
  cry() {},
  eat() {},
}

这里p实际上也是被编译为一个对象。

5.4.2 接口与类之间的继承

接口继承类,然后再用一个类来继承这个接口:

class Auto {
  state = 1
}

interface AutoInterface extends Auto {}

class C implements AutoInterface {
  state = 1 
}

在此,如果C不定义state,将会报错。

下图反映了接口和类的关系: