TypeScript 学习之Class

基本使用

class Greeter {
  // 属性
  greeting: string;
  // 构造函数
  constructor(message: string) {
    // 用this 访问类的属性
    this.greeting = message;
  }

  // 方法
  greet() {
    return 'Hello, ' + this.greeting;
  }
}
// 实例化
let greeter = new Greeter('World');

声明了一个Greeter类,类有三个成员:一个是 greeting属性,一个构造函数和一个greet方法。使用new关键字实例化Greeter对象。实例化时会调用类的构造函数,并返回一个对象。

继承

使用 extends 关键字。
派生类(子类) 包含构造函数就必须调用super()

class Animal {
  move(distanceInMeters: number = 0) {
    console.log(`Animal moved ${distanceInMeters}`);
  }
}

class Dog extends Animal {
  bark() {
    console.log('Woof! Woof');
  }
}

const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

最基本的继承:类从基类中继承了属性和方法。

class Animal {
  name: string;
  constructor(theName: string) {
    this.name = theName;
  }

  move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}`);
  }
}

class Snake extends Animal {
  constructor(name: string) {
    super(name);
  }
  // 重写父类move方法
  move(distanceInMeters = 5) {
    console.log('Slithering....');
    super.move(distanceInMeters);
  }
}

class Horse extends Animal {
  constructor(name: string) {
    super(name);
  }
  // 重写父类move方法
  move(distanceInMeters = 45) {
    console.log('Galloping....');
    super.move(distanceInMeters);
  }
}

let sam = new Snake('Sammy the Python');
let tom: Animal = new Horse('Tommy the Palomino');

sam.move();
tom.move(34);

公有、私有与受保护的修饰符

在类中所有成员都默认被公有public修饰符修饰。

  • public 公有
    • 公有(public)成员可能在类的外部访问
class Animal {
  public name: string;
  public constructor(theName: string) {
    this.name = theName;
  }
  public move(distanceInMeters: number) {
    console.log(`${this.name} moved ${distanceInMeters}`);
  }
}

let animal = new Animal('dog');
animal.name = 'Cat';
animal.move(20);
  • private 私有
    • 当成员被标记成private时,属性或方法就不能在类的外部访问
    • 子类不能访问父类的 private 修饰的属性
    • constructor 可以被 private 修饰,但是这样类就不能被实例化,也不能被子类继承
class Animal {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}

class Dog extends Animal {
  constructor(theName: string) {
    super(theName);
  }

  move() {
    console.log(`${this.name}`); // 报错,子类不能访问父类的私有属性
  }
}
let animal = new Animal('dog');
animal.name = 'cat'; // 报错,name 是私有属性,类外不能访问
  • 如果两个的类型的所有成员的类型都是兼容的,那么这两类型是兼容的。但是这两个类型的其中一个类型包含private成员,那么只有当另一个类型中也存在这样一个 private成员,并且来自同一处声明时,才能认为这两个类型是兼容的 protected成员也使用这个规则
class Animal {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}

class Rhino extends Animal {
  constructor() {
    super('Rhino');
  }
}

class Employee {
  private name: string;

  constructor(theName: string) {
    this.name = theName;
  }
}

let animal = new Animal('Goat');
let rhino = new Rhino();
let employee = new Employee('Bob');

animal = rhino;
animal = employee; // 错误:Animal 与 Employee 不兼容
  • protected 保护
    • 子类可以访问父类的 protected 修饰的成员。但是protected修饰的成员不能再类的外部访问
    • constructor可以被protected修饰,但是类不能被实例化,可以被子类继承
class Person {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Employee extends Person {
  private department: string;

  constructor(name: string, department: string) {
    super(name);
    this.department = department;
  }

  public getElevatorPitch() {
    return `Hello, my name is ${this.name} and I work in ${this.department}`;
  }
}

let howard = new Employee('Howard', 'Sales');
console.log(howard.getElevatorPitch());
console.log(howard.name); // 错误,protected 修饰的属性不能再类的外部访问

readonly 修饰符

  • readonly关键字将属性设置为只读的,只读属性必须在声明时或构造函数里被初始化。
class Octopus {
  readonly name: string;
  readonly numberOfLegs: number = 8;
  constructor(theName: string) {
    this.name = theName;
  }
}

let dad = new Octopus('Man with the 8 strong legs');
dad.name = 'Man with the 3-piece suit'; // 错误,name是只读的

参数属性

参数属性通过给构造函数参数前面添加一个访问限定符来声明。
会同时声明并初始化一个成员

  • 使用readonly修饰的属性与构造函数的参数结合。
class Octopus {
  readonly numberOfLegs: number = 8;
  constructor(readonly name: string) {}
}

let dad = new Octopus('Man with the 8 strong legs');
dad.name = 'Man with the 3-piece suit'; // 错误 namename 只读

console.log(dad.name);
  • 使用 public修饰的属性与构造函数的参数结合。
class Octopus {
  readonly numberOfLegs: number = 8;

  constructor(public name: string) {}
}

let dad = new Octopus('man');
console.log(dad.name);
  • 使用 private修饰的属性与构造函数的参数结合。
class Octopus {
  readonly numberOfLegs: number = 8;

  constructor(private name: string) {}

  getName() {
    return this.name;
  }
}

let dad = new Octopus('man');
console.log(dad.getName());
  • 使用 protected修饰的属性与构造函数的参数结合。
class Octopus {
  readonly numberOfLegs: number = 8;

  constructor(protected name: string) {}

  getName() {
    return this.name;
  }
}

let dad = new Octopus('man');
console.log(dad.getName());

存储器

typescript 支持通过 getters/setters 来截取对对象成员的访问。
只有get没有set的会自动被推断为readonly

let passcode = 'secret passcode';

class Employee {
  private _fullName: string;

  get fullName(): string {
    return this._fullName;
  }

  set fullName(newName: string) {
    if (passcode && passcode == 'secret passcode') {
      this._fullName = newName;
    } else {
      console.log('Error: Unauthorized update of employee!');
    }
  }
}

let employee = new Employee();
employee.fullName = 'Bob Smith';

if (employee.fullName) {
  alert(employee.fullName);
}

静态属性

关键字: static
静态属性是存在于类本身,不存在与类的实例。
同一个类的实例对象共享类上的静态属性的状态。
通过类名.方式访问静态属性

class Grid {
  static origin = {x: 0, y: 0};

  constructor(public scale: number) {}

  calculateDistanceFromOrigin(point: {x: number; y: number}) {
    let xDist = point.x - Grid.origin.x;
    let yDist = point.y - Grid.origin.y;

    return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
  }
}

let grid1 = new Grid(1.0);
let grid2 = new Grid(5.0);

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

抽象类

关键字: abstract
抽象类不能直接被实例化,只能通过派生类(子类)继承,实例化派生类(子类)。
抽象类类似于接口,但是抽象类可以包含成员的实现细节。
抽象类的抽象方法不包含具体实现并且必须在派生类(子类)中实现。
抽象方法必须在方法前声明abstract关键字并且可以包含访问修饰符

abstract class Deparment {
  constructor(public name: string) {}

  printName(): void {
    console.log('Department name: ' + this.name);
  }

  abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Deparment {
  constructor() {
    super('Accounting and Auditing'); // 在派生类的构造函数必须调用
  }

  printMeeting(): void {
    console.log('The Accounting Department meets each Monday at 10am.');
  }

  generateReports(): void {
    console.log('Generating accounting reports...');
  }
}

let department: Deparment; // 允许创建一个抽象类型的引用
// department = new Deparment(); // 错误 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许创建一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误:方法在声明的抽象类中不存在

构造函数

  • 类的实例化会调用类的构造函数
class Greeter {
  static standardGreeting = 'Hello, there';
  greeting: string;
  greet() {
    if (this.greeting) {
      return 'Hello, ' + this.greeting;
    } else {
      return Greeter.standardGreeting;
    }
  }
}

let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());

let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = 'Hey there!';

let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());

特别注意:创建一个greeterMaker的变量,这个变量保存了这个类或者说保存了类构造函数。然后用typeof greeter,意思是取Greeter类的类型,而不是实例的类型,也就是构造函数的类型。这个类型包含了类的所有静态成员和构造函数。

类当作接口使用

类定义会创建类的实例类型和一个构造函数

class Point {
  x: number;
  y: number;
}
interface Point3d extends Point {
  z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值