TS学习笔记(三):类

3,541 阅读2分钟

传统的 JavaScript 程序使用函数和基于原型的继承来创建可重用的组件,从 ES6 开始,JavaScript 程序能够使用基于类的面向对象的方式。使用 TypeScript,你可以使用 ES6 中规定的新特性,编译后的 JavaScript 可以在所有主流浏览器和平台上运行。

基本用法

class Person {
  public love: string;
  constructor(love: string) {
    this.love = love;
  }
  public sayLove() {
    console.log(`my love is ${this.love}`)
  }
}

继承

在构造器里访问 this 的属性之前,一定要调用 super() ,这个是 TypeScript 强制执行的一条重要规则。

class Person {
  public love: string;
  constructor(love: string) {
    this.love = love;
  }
  public sayLove() {
    console.log(`my love is ${this.love}`)
  }
}

class SuperPerson extends Person {
  public name: string;
  constructor(love: string, name: string) {
    super(love);
    this.name = name;
  }
  public sayName(){
    console.log(`my name is ${this.name}`)
  }
}

let me = new SuperPerson('HTML', 'funlee');
me.sayLove()
me.sayName()

访问控制

public、private、protected

默认是 public, 不再赘述,参考前面例子。

当成员标记为 private 时,它就不能在声明它的类的外部访问,用 protected 修饰的属性依然如此。

class Person {
  private love: string; // or  prot
  constructor(love: string) {
    this.love = love;
  }
  public sayLove() {
    console.log(`my love is ${this.love}`)
  }
}

let me = new Person('TS');
me.love = 'JS'; // error

private 和 protected 有一点不同, protected 成员在派生类中仍然可以访问。例如:

class Person {
  protected name: string; 
  constructor(name: string) {
    this.name = name;
  }
}
class Man extends Person {
  private love: string;
  constructor(name: string, love: string) {
    super(name);
    this.love = love;
  }
  public say() {
    // 如果Person 中用 private 修饰 name 则不能访问到 name 属性
    console.log(`my name is ${this.name}, and my love is ${this.love}`);
  }
}
let me = new Man('funlee', 'TS');

注意:TypeScript 使用的是结构性类型系统,所以当比较两种不同的类型时,如果所有的成员的类型都是兼容的,那么这两个类型就是兼容的。如:

class A {
  prop1: string
}
class B {
  prop1: string
  prop2: string
}
let instance:A = new B() // 允许这么做,因为A的所有成员类型,B中都有

但是如果被比较的类里面含有 private 和 protected 类型成员的时候,情况就不同了,这时候需要另一个类里也含有相应的 private 或 protected 成员,类型才能是兼容的,所以有:

class A {
  private prop1: string
}
class B {
  private prop2: string
}
let p1:A = new B() // 报错
class C extends A {

}
let p2:A = new C() // 允许这么做

readonly

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

class Person {
  readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}
let me = new Person('funlee');
me.name = 'new name'; // error

参数属性

参数属性允许同时创建初始化成员,可以把声明和赋值合并至一处,如:

class Person {
  constructor(public name: string, protected love: string, readonly age: number, private weight: string) {
    this.name = name;
    this.love = love;
    this.age = age;
  }
  public sayWeight() {
    console.log(`my weight is ${this.weight}`)
  }
}
let me = new Person('funlee', 'TS', 18, '55kg');
me.sayWeight()

存取器

TypeScript 支持 getter 和 setter,但是有一点限制:编译器输出必须设为 ES5 或者更高,不支持降级到 ES3,另外,当一个存取器只带有 get 却不带有 set 时,它会被自动推断为 readonly。

class Person {
  public _love: string;
  constructor(love: string) {
    this._love = love;
  }
  get love(): string{
    return this._love;
  }
  set love(newLove: string) {
    this._love = `error!! my love can't be chenged`;
  }
}
let me = new Person('TS');
console.log(me.love); // TS
me.love = 'HTML';
console.log(me.love); // error!! my love can't be chenged

静态属性

可以使用static来定义类里的静态属性,静态属性属于类自身,而不属于实例,访问的时候要用类名访问,而不能用实例对象访问,如:

class Person {
  static love: string = 'TS';
}
let me = new Person();
console.log(Person.love); // TS
console.log(me.love); // error

抽象类

抽象类只能作为其他派生类的基类使用,抽象类不能被实例化,它具有如下特点:

  • 抽象类可以包含成员的实现细节,且抽象类必须用 abstract 声明
  • 抽象类里不含方法体的方法称为抽象方法,使用 abstract 声明,抽象方法必须被子类实现(抽象方法必须使用 abstract 关键字声明,且可以包含访问修饰符)
abstract class Person {
  public love: string;
  constructor(love: string) {
    this.love = love;
  }
  abstract sayLove(): string; // 必须在派生类中实现
}
class Man extends Person{
  constructor(love: string){
    super(love)
  }
  sayLove() {
    return `my love is ${this.love}`;
  }
}
let me = new Man('TS');
console.log(me.sayLove()); // my love is TS

把类当做接口使用

类定义会创建两个东西:类的实例类型和一个构造函数,因为类可以创建出类型,所以能够在允许使用接口的地方使用类。

class Person {
  name: string;
  age: number;
}
interface Man extends Person {
  love: string;
}
let me: Man = {
  name: 'funlee',
  age: 18,
  love: 'TS'
}