TypeScript(五)类

114 阅读5分钟

typescript 是面向对象的 javascript,类描述了所创建的对象共同的属性和方法,typescript 支持面向对象的所有特性,比如 类、接口等。定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块。

字段

字段是类里面声明的变量,字段表示对象的有关数据。

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

构造函数 constructor

类实例化的时候调用,可以为类的对象分配内存。

class Rect {
  constructor(public x: number, public y: number) {}
  getPos() {
    return { x: this.x, y: this.y };
  }
}
let position = new Rect(100, 100);
position.getPos();

方法

方法为对象要执行的操作

class Rect {
  x: number;
  y: number;

  getPos() {
    return { x: this.x, y: this.y };
  }
}

类的继承

typescript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。类继承使用关键字 extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。typescript 一次只能继承一个类,不支持继承多个类,但 typescript 支持多重继承(A 继承 B,B 继承 C)。

B 继承了 A,所以 B 的实例有 A 中的 print 方法。

class A {
  print() {
    console.log("Hello!!!");
  }
}

class B extends A {}
const b = new B();

b.print();

需要注意的是子类只能继承一个父类,typescript 不支持继承多个类,但支持多重继承。

class A {
  print() {
    console.log("Hello!!!");
  }
}
class B extends A {}
class C extends B {}
const c = new C();

c.print();

继承类的方法重写

子类可以覆盖基类的同名方法,这个过程称之为方法的重写。下面子类 C 重写了基类 A 的同名方法 print

class A {
  print(): void {
    console.log("Hello!!!");
  }
}
class B extends A {}
class C extends B {
  print(name?: string): void {
    if (name === void 0) {
      super.print();
    } else {
      console.log(`Print name is ${name}`);
    }
  }
}
const c = new C();

c.print("rinvay");

类和接口

类可以 使用关键字 implements 实现接口,接口字段作为类的属性使用。

implements 关键字

interfacetype 都可以定义一个对象类型。类使用 implements 关键字,表示该类的实例对象满足这个对象类型。

interface Rect {
  x: number;
  y: number;
}
type Point = {
  x: number;
  y: number;
};

class A implements Rect {
  x = 0;
  y = 0;
}

class B implements Rect {
  x = 100;
  y = 100;
}

类可以实现多个接口(其实是接受多重限制),每个接口之间使用逗号分隔。

interface Rect {
  x: number;
  y: number;
}
type Point = {
  z: number;
};

class A implements Rect, Point {
  x = 0;
  y = 0;
  z = 0;
}

同时实现多个接口并不是一个好的写法,容易使得代码变得难以维护,可以使用下面的方法替代。

interface Rect {
  x: number;
  y: number;
}
interface Point {
  z: number;
}

interface Coordinate extends Rect, Point {}

class A implements Coordinate {
  x = 0;
  y = 0;
  z = 0;
}

抽象类

TypeScript 允许在类的定义前面加上关键字 abstract,表示该类不能被实例化,只能当作其他类的模板,这种类就叫做“抽象类”。

abstract class A {}

const a = new A(); // error 无法创建抽象类的实例。

抽象类的内部可以有已经实现好的属性和方法,也可以有还未实现的属性和方法。后者在前面加上关键字abstract 之后就叫做“抽象成员”,表示该方法需要子类实现。如果子类没有实现抽象成员就会报错,抽象成员只能存在于抽象类中。

abstract class A {
  abstract print(): void;
}
class B extends A {} // error 非抽象类‘B’不实现‘A’的所有抽象成员

// ok
abstract class A {
  abstract print(): void;
}
class B extends A {
  print(): void {
    console.log("Hello!!!");
  }
}

static 关键字

static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。

class StaticRect {
  static x: number;
  static y: number;
  static display(): void {
    console.log(StaticRect.x, StaticRect.y);
  }
}

StaticRect.x = 100;
StaticRect.y = 100;
StaticRect.display();

const rect = new StaticRect();
rect.display(); // error 属性“display”在类型“StaticRect”上不存在。

上面示例中,x,y 是静态属性,display 是静态方法。它们都必须通过 StaticRect 获取,而不能通过实例对象调用,static 关键字前面可以使用 publicprivateprotected 修饰符。

可访问性修饰符

TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限。

public(默认)

公有成员,可以在任何地方被访问。

class Rect {
  public x: number = 100;
  public y: number = 100;
  public display(): void {
    console.log(this.x, this.y);
  }
}

const rect = new Rect();

rect.x = 200;
rect.y = 200;

rect.display();

protected

受保护成员,可以被其自身以及其子类访问,实例无法使用该成员,但是子类内部可以使用。

class Rect {
  protected x: number = 100;
  public y: number = 100;
  public display(): void {
    console.log(this.x, this.y);
  }
}

class A extends Rect {
  print() {
    console.log(this.x); // ok
  }
}

const rect = new A();

rect.x; // error 属性“x”受保护,只能在类“Rect”及其子类中访问。
rect.y; // ok

子类 A 中 能访问受保护成员 x ,实例 rect 中不能通过 rect.x 访问受保护成员 x

private

私有成员,只能被其定义所在的类访问,类的实例和子类都不能使用该成员。

class Rect {
  private x: number = 100;
  public y: number = 100;
  public display(): void {
    console.log(this.x, this.y);
  }
}

class A extends Rect {
  print() {
    console.log(this.x); // error 属性“x”为私有属性,只能在类“Rect”中访问。
  }
}

const rect = new A();

rect.x; // error 属性“x”为私有属性,只能在类“Rect”中访问。
rect["x"]; // ok

rect.y;
rect.print();

子类 A 中 不能访问私有变量 x ,实例 rect 中也不能通过 rect.x 访问私有变量 x。但是 可以 通过 rect["x"] 访问,所以严格地说,private 定义的私有变量,并不是真正意义的私有变量。在编译成 js 之后,private 关键字就掉了,这时外部访问该私有变量就不会报错。而 es6 引入了真正意义上的私有变量写法#prop。因此建议不使用 private,改用 es6 写法,获得真正意义上的私有变量。