Notes
TypeScript
类(Class)

面向对象

  • 类(class):抽象的概念,是一类事物的抽象,是一类事物的模板,是一类事物的蓝图
  • 对象(object):具体的事物,是类的实例,是类的具体表现
  • 面向对象(OOP):是一种编程思想,是一种解决问题的思路和方法,有三大特征:封装、继承、多态

类的定义

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  run() {
    return `${this.name} is running`;
  }
}

上面的代码定义了一个类 Animal,它包含了两个成员:namerun。其中,name 是一个实例属性,run 是一个实例方法。注意,实例属性必须有初始值或者在构造函数中被初始化

constructor 是构造函数,它会在类被实例化的时候被调用,用来初始化实例属性。上面的代码中,constructor 接受一个 name 参数,然后把它赋值给 name 实例属性,这个过程是在实例化的时候自动完成的。

我们可以创建一个 Animal 的实例:

const snake = new Animal('lily');
console.log(snake.run()); // lily is running

:::tip ts-node ts-node 是一个 TypeScript 的执行环境,可以直接在命令行中运行 TypeScript 代码,不需要编译成 js 文件。如果你使用的是 ts-node,那么你可以直接在命令行中运行上面的代码,不需要编译成 js 文件。实际上是 ts-node 帮我们编译成了 js 文件,然后再执行的。 :::

类的继承

class Dog extends Animal {
  bark() {
    return `${this.name} is barking`;
  }
}

然后调用 Dog 的实例方法:

const xiaobao = new Dog('xiaobao');
console.log(xiaobao.run()); // xiaobao is running
console.log(xiaobao.bark()); // xiaobao is barking

还可以重写构造函数:

class Cat extends Animal {
  constructor(name) {
    super(name);
    console.log(this.name);
  }
  run() {
    return 'Meow, ' + super.run();  // 注意这里的 super
  }
}
 
const maomao = new Cat('maomao');
console.log(maomao.run()); // Meow, maomao is running

在构造函数中,必须使用 super 调用父类的构造函数,否则会报错。这是因为子类实例的创建,是基于父类实例的创建的,如果不调用 super,子类实例就得不到 this 对象,而 this 对象是实例化类时最为重要的一个环节。

类的修饰符

TypeScript 中有三种修饰符:publicprotectedprivate

public

public 修饰符是默认的修饰符,可以省略不写。public 修饰符修饰的属性或方法是公有的,可以在任何地方被访问到。

class Animal {
  public name: string;
  public constructor(name: string) {
    this.name = name;
  }
  public run() {
    return `${this.name} is running`;
  }
}
 
const snake = new Animal('lily');
// 更新 name 属性
snake.name = 'lucy'; // 正确

praivate

private 修饰符修饰的属性或方法是私有的,不能在声明它的类的外部访问。在子类中也是不允许访问的

class Animal {
  private name: string;
  public constructor(name: string) {
    this.name = name;
  }
  public run() {
    return `${this.name} is running`;
  }
}
 
const snake = new Animal('lily');
// 更新 name 属性
snake.name = 'lucy'; // 报错

protected

protected 修饰符修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

class Animal {
  protected name: string;
  public constructor(name: string) {
    this.name = name;
  }
  public run() {
    return `${this.name} is running`;
  }
}
 
// 更新 name 属性
snake.name = 'lucy'; // 报错
 
 
// 在子类中访问
 
class Dog extends Animal {
  bark() {
    return `${this.name} is barking`;   // 正确
  }
}
 
const xiaobao = new Dog('xiaobao');
console.log(xiaobao.bark()); // xiaobao is barking

readonly

readonly 修饰符修饰的属性只读,不能被修改。由于 readonly 修饰符修饰的属性是只读的,所以必须在声明时或构造函数中被初始化。被它修饰的属性和方法被称为静态属性和方法。

class Animal {
  public readonly name: string;
  public constructor(name: string) {
    this.name = name;
  }
}
 
const snake = new Animal('lily');
// 更新 name 属性
snake.name = 'lucy'; // 报错

下面是一个静态方法的例子:

class Animal {
  public static categories: string[] = ['mammal', 'bird'];
  public static isAnimal(a) {
    return a instanceof Animal;
  }
}
 
console.log(Animal.categories); // ['mammal', 'bird']

类和接口

接口可以对类的一部分行为进行抽象,只要类实现了接口中的方法,就可以认为它实现了这个接口。

在下面的例子中,carcellphone 都实现了 Radio 接口,所以它们都有 switchRadio 方法。我们可以把 Radio 接口看成是一个规范,只要满足这个规范的类都可以实现这个接口。

class Car{
    switchRadio(trigger: boolean) {
        // ...
    }
}
 
class Cellphone {
    switchRadio(trigger: boolean) {
        // ...
    }
}

实现 Radio 接口:

interface Radio {
    switchRadio(trigger: boolean): void;
}

CarCellphone 类中使用 implements 关键字实现 Radio 接口:

class Car implements Radio {
    switchRadio(trigger: boolean) {
        // ...
    }
}
 
class Cellphone implements Radio {
    switchRadio(trigger: boolean) {
        // ...
    }
}

备注:如果不实现 Radio 接口中的 switchRadio 方法,那么 Car 类就会报错。

可以实现多个接口,用逗号隔开:

interface Radio {
    switchRadio(trigger: boolean): void;
}
 
interface Battery {
    checkBatteryStatus(): void;
}
 
class cellPhone implements Radio, Battery {
    switchRadio(trigger: boolean) {
        // ...
    }
 
    checkBatteryStatus() {
        // ...
    }
}

甚至可以将接口继承自另一个接口:

interface Radio {
    switchRadio(trigger: boolean): void;
}
 
interface Battery {
    checkBatteryStatus(): void;
}
 
interface RadioWithBattery extends Radio {
    checkBatteryStatus(): void;
}
 
class cellPhone implements RadioWithBattery {   // 这里只用写 RadioWithBattery 接口就可以了
    switchRadio(trigger: boolean) {
        // ...
    }
 
    checkBatteryStatus() {
        // ...
    }
}