TypeScript Class类的用法

161 阅读2分钟

背景简介

传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手, 因为他们用的是基于类的继承并且对象是由类构建出来的。 从ECMAScript 2015,也就是ECMAScript 6开始, JavaScript程序员将能够使用基于类的面向对象的方式。 使用TypeScript,我们允许开发者现在就使用这些特性, 并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript版本。

风格约定

  • 1.类名首字母大写
  • 2.使用new关键字对类实例化操作

成员结构

class Person {
  name:string; // 1.属性
  constructor(msg:string){ // 2.构造函数
    this.name=msg;
  }
  sayHello(){ // 3.方法
    return `hello,${this.name}`
  }
}
let p1=new Person('小宁');
console.log(p1.sayHello())

继承

class Animal { // Animal在这里称为基类或超类
  move(distanceInMeters: number = 0) {
      console.log(`Animal moved ${distanceInMeters}m.`);
  }
}
class Dog extends Animal { // Dog称为派生类或子类
  bark() {
      console.log('Woof! Woof!');
  }
}
const dog = new Dog();
dog.bark();
dog.move(10);

继承使用super关键字

class Animal {
  type:string;
  constructor(msg:string) {
    this.type=msg;
  }
  sayName(){
    console.log(`我的类型为:${this.type}`)
  }
}
class Dog extends Animal {
  constructor(name:string){super(name)}
}
class Horse extends Animal {
  constructor(name:string){super(name)}
}
let d1=new Dog('小狗');
// 即使d2被声明为Dog类型,但因为它的值是Horse,它会调用 Horse里重写的方法:
let d2:Dog=new Horse('小马');
d1.sayName()
d2.sayName();

公共、私有、受保护

  • 公共 public(默认)
class Animal {
  public name: string;
  public constructor(theName: string) { this.name = theName; }
  public move(distanceInMeters: number) {
      console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
  • 私有 private

当成员被标记成 private时,它就不能在声明它的类的外部访问

class Animal {
  private name: string;
  constructor(msg: string) { this.name = msg; }
}
let a1=new Animal('小牛');
console.log(a1.name) // 报错 提示name为私有属性
  • 受保护 protected

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问

class Animal {
  protected name: string;
  constructor(msg: string) { this.name = msg; }
}
class Cat extends Animal {
  constructor(name:string){super(name)}
  public print():string{
    return `父类的name为:${this.name}`;
  }
}
let c1=new Cat('小猫');
console.log(c1.name) // 不可外部访问父类或者子类的protected属性,会提示只可内部访问
let a1=new Cat('动物');
console.log(a1.print()); // 在子类的内部方法中,是可以访问父类protected成员的

只读属性

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

class Person { // 情况1:正常代码
  readonly name:string;
  readonly age:number=20;
  constructor(msg:string){
    this.name=msg;
    console.log(this.name)
  }
}
let p1=new Person('小明');
class Person { // 情况2:readonly简化声明赋值代码
  readonly age:number=20;
  constructor(readonly name:string){
    console.log(this.name)
  }
}
let p1=new Person('小红');

存取器

TS支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问

class DateSource {
  private _userName:string='default';
  get username():string{
    return this._userName;
  }
  set username(msg:string){
    this._userName=msg
  }
}
let d1=new DateSource();
console.log(d1.username) // 使用get实现访问外部不可访问的变量
d1.username='update'; // 使用set实现修改赋值操作
console.log(d1.username) // 再次打印查看效果

静态属性

超类中static修饰的属性不可被this.访问,只可以使用类名.访问·

class Person {
  static width:number=200;
  public printStyle(){
    console.log(`打印width==${this.width}`) // 报错 不可以使用this.
    console.log(`打印width==${Person.width}`) // 正确 使用类名.格式
  }
}
let p1=new Person();
p1.printStyle();

抽象类

使用abstract关键字

abstract class Person { // abstract声明抽象类
  public name:string;
  constructor(msg:string){this.name=msg};
  abstract printMeeting():void; // abstract实现方法
}
class Man extends Person {
  constructor(){super('man')}
  printMeeting():void{ // 子类必须实现父类创造的方法,因为那是父类给子类定义的标准
    console.log('man必须实现方法体')
  }
}
let p1:Person; // 正确 可以引用抽象类的类型
let p1=new Man(); // 正确 允许实例抽象类的子类
let p1=new Person('人'); // 错误 抽象类不可以被实例,只能继承

把类当做接口使用

class Point {
  x: number;
  y: number;
}
interface Point3d extends Point {
  z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};