一、概述

传统方法中,JavaScript 通过构造函数实现类的概念,通过原型链实现继承。在 ES6 中,我们迎来了 Class。TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。

二、示例

1. 属性修饰符

修飾符 描述
public 修饰公共属性/方法,可以在任何位置访问,所有属性和方法默认为 puiblic
private 修饰私有属性/方法,不能在声明它的类的外部访问。
protected 修饰的属性或方法是受保护的,它和 private
类似,区别是它在子类中也是允许被访问的。
readonly 只读属性,必须在声明时或构造函数里被初始化。
static 静态属性(类属性/类方法)→ 通过类名调用

我们来看一组示例:

  1. // -- 定义一个Person 类
  2. class Person {
  3. /** 定义静态属性并直接赋值(TS会自动推断country类型为string) */
  4. static country = "中国";
  5. /** 定义一个公共属性(注意:public关键字可省略) */
  6. public name: string;
  7. /** 定义一个私有属性,构造实例时可以赋值,但是不能通过实例访问 */
  8. private job: string;
  9. /** 定义一个受保护的属性,只允许在Perosn及其子类中访问 */
  10. protected birth: string;
  11. /** 定义一个只读属性,只允许在构造时赋值 */
  12. readonly gender: number;
  13. /** 构造函数 */
  14. constructor(name: string, job: string, birth: string, gender: number) {
  15. this.name = name;
  16. this.job = job;
  17. this.birth = birth;
  18. this.gender = gender;
  19. }
  20. }
  21. const person = new Person("Li-HONGYAO", "前端工程师", "1993/07/16", 1);
  22. console.log(Person.country); // 中国
  23. console.log(person.name); // Li-HONGYAO
  24. console.log(person.job); // Property 'job' is private and only accessible within class 'Person'.
  25. console.log(person.birth); // Property 'birth' is protected and only accessible within class 'Person' and its subclasses.
  26. person.gender = 0; // annot assign to 'gender' because it is a read-only property.

在 TypeScript 中,构造器允许简写:

  1. class Person {
  2. constructor(public name: string, private job: string, protected birth: string, readonly gender: number) {}
  3. }
  4. const person = new Person("Li-HONGYAO", "前端工程师", "1993/07/16", 1);
  5. console.log(person.name); // Li-HONGYAO
  6. console.log(person.job); // Property 'job' is private and only accessible within class 'Person'.
  7. console.log(person.birth); // Property 'birth' is protected and only accessible within class 'Person' and its subclasses.
  8. person.gender = 0; // annot assign to 'gender' because it is a read-only property.

2. 抽象类

abstract 用于定义抽象类和其中的抽象方法。什么是抽象类?

  • 首先,抽象类是不允许被实例化的
  • 其次,抽象类中的抽象方法必须被子类实现
  1. // -- 定义一个抽象类
  2. abstract class Person {
  3. constructor(name: string) {}
  4. /** 抽象属性,不用赋值,要求在子类中必须定义此属性并为其赋值 */
  5. abstract job: string;
  6. /** 抽象方法,不包含具体实现,要求在之类中必须实现此方法 */
  7. abstract sayHello(name: string): void;
  8. /** 非抽象方法,无需要求子类实现,但是子类可以重写此方法 */
  9. running() {
  10. console.log("I'm running!");
  11. }
  12. }
  13. class Teacher extends Person {
  14. // Non-abstract class 'Teacher' does not implement inherited abstract member 'job' from class 'Person'.
  15. // Non-abstract class 'Teacher' does not implement inherited abstract member 'sayHello' from class 'Person'.
  16. }
  17. class Student extends Person {
  18. /** 实现抽象属性 */
  19. job: string;
  20. name: string;
  21. constructor(name: string, job: string) {
  22. super(name);
  23. this.job = job;
  24. this.name = name;
  25. }
  26. /** 实现抽象方法 */
  27. sayHello(name: string) {
  28. console.log(`Hello, ${name}!`);
  29. }
  30. /** 重写抽象类方法 */
  31. running() {
  32. console.log("I'm studing!")
  33. }
  34. }
  35. const stu = new Student("Li-HONGYAO", '前端工程师');
  36. console.log(stu.job); // 前端工程师
  37. console.log(stu.name); // Li-HONGYAO
  38. stu.sayHello("Li-HONGYAO"); // Hello, Li-HONGYAO!
  39. stu.running(); // "I'm studing!"

三、类与接口

接口(Interfaces)可以用于对「对象的形状(Shape)」进行描述,通常,通过接口可以约束一个类的形状,只需要让这个类实现这个接口即可。

1. 类实现接口

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

举例来说,人是一个类,学生是人的子类。学生具备姓名属性和说话的能力,我们可以给学生添加一个姓名属性和说话的方法。这时候如果有另一个类,老师,也有姓名属性和说话的能力,这个时候,就可以把姓名属性和说话的方法提取出来,作为一个接口,老师和学生都去实现它:

  1. interface Person {
  2. name: string
  3. speack: () => void;
  4. }
  5. class Teacher implements Person {
  6. name: string;
  7. constructor(name: string) {
  8. this.name = name;
  9. }
  10. speack() {
  11. console.log("我是一名老师!");
  12. }
  13. }
  14. class Student implements Person {
  15. name: string;
  16. constructor(name: string) {
  17. this.name = name;
  18. }
  19. speack() {
  20. console.log("我是一名学生!");
  21. }
  22. }

一个类可实现多个接口,中间以逗号 , 隔开:

  1. interface Person {
  2. name: string,
  3. speack: () => void;
  4. }
  5. interface Teach {
  6. course: string;
  7. }
  8. class Teacher implements Person, Teach {
  9. name: string;
  10. course: string;
  11. constructor(name: string, course: string) {
  12. this.name = name;
  13. this.course = course;
  14. }
  15. speack() {
  16. console.log(`我是一名${this.course}老师!`);
  17. }
  18. }

上述示例中,Teacher 类实现了Person 和 Teach 接口。

2. 接口继承类

  1. class Point {
  2. x: number = 0;
  3. y: number = 0;
  4. }
  5. interface Point3d extends Point {
  6. z: number;
  7. }
  8. let point3d: Point3d = { x: 1, y: 2, z: 3 };

四、单例模式

接下来我们看看如何在 TypeScript 中定义一个单例:

class Singleton {
  name: string = '';
  private static instance: Singleton;
  private constructor() {}
  static defaultSingleton() {
    if(!this.instance) {
      this.instance = new Singleton();
    }
    return this.instance;
  }
}

const single1 = Singleton.defaultSingleton();
const single2 = Singleton.defaultSingleton();

console.log(single1 === single2); // true

single1.name = 'Muzili';
console.log(single2.name); // Muzili