阅读 68

TypeScript | 类 Class

类描述了所创建的对象共同的属性和方法。通过 class 关键字声明一个类,主要包含以下模块:

  • 属性
  • 构造函数
  • 方法

相关概念

  • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  • 对象(Object):类的实例,通过 new 生成
  • 面向对象(OOP)的三大特性:封装、继承、多态
  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat
  • 存取器(getter & setter):用以改变属性的读取和赋值行为
  • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
  • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
  • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口

属性和方法

使用 class 定义类,使用 constructor 定义构造函数。

通过 new 生成新实例的时候,会自动调用构造函数。

class Animal {
  public name;
  constructor(name) {
      this.name = name;
  }
  sayHi() {
      return `My name is ${this.name}`;
  }
}

let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
复制代码

存取器

使用 getter 和 setter 可以改变属性的赋值和读取行为

class Animal {
  constructor(name) {
    this.name = name;
  }
  get name() {
    return 'Jack';
  }
  set name(value) {
    console.log('setter: ' + value);
  }
}

let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
复制代码

类的继承

使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法。这样可以抽出公共部分让子类复用。

class Cat extends Animal {
  constructor(name) {
    super(name); // 调用父类的 constructor(name)
    console.log(this.name);
  }
  sayHi() {
    return 'Meow, ' + super.sayHi(); // 调用父类的 sayHi()
  }
}

let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
复制代码

访问修饰符

public

public 修饰的属性或方法是公有的,在程序类的外部可以访问。类的成员全部默认为 public

class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
复制代码

private

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

image.png

当构造函数修饰为 private 时,该类不允许被继承或者实例化:

image.png

protected

protected 修饰的属性或方法是受保护的,只能被类的内部以及类的子类访问。

class Animal {
  protected name; 
  public constructor(name) {
    this.name = name;
  }
}
class Cat extends Animal {
  constructor(name) {
    super(name);
    console.log(this.name); // name 在子类中允许访问
  }
}
复制代码

当构造函数修饰为 protected 时,该类允许被继承,不允许被实例化:

image.png

readonly

通过 readonly 关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。

image.png

静态方法

使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用,类的实例无法访问:

image.png

静态方法调用同一个类中的其他静态方法,可使用 this 关键字:

class StaticMethodCall {

  static staticMethod() {
      return 'Static method has been called'
  }
  
  //静态方法中的 this 指向类本身,而静态方法也存在于类本身
  //所以可以在静态方法中用 this 访问在同一类中的其他静态方法
  static anotherStaticMethod() {
      return this.staticMethod() + ' from another static method'
  }
}
复制代码

非静态方法中,不能直接使用 this 关键字来访问静态方法。而要用类本身或者构造函数的属性来调用该方法:

class StaticMethodCall {
  constructor() {
      // 类本身调用
      console.log(StaticMethodCall.staticMethod())

      // 构造函数的属性调用
      console.log(this.constructor.staticMethod())
  }
  static staticMethod() {
      return 'static method has been called.'
  }
}
复制代码

类指向其构造函数本身,在非静态方法中,this.constructor === StaticMethodCall 为 true

抽象类

abstract 用于定义抽象类、抽象类内部定义抽象方法。

抽象类不允许被实例化,抽象类中的抽象方法必须被子类实现

image.png

抽象类中的抽象方法被子类实现后不报错了

image.png

把类当做接口使用

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};
复制代码

学习链接

文章分类
前端
文章标签