作者: 竹林_ | 来源:发表于2019-10-16 11:52 被阅读0次

关键字 class

class Greeter {
  greetering: string;
  constructor(message: string){
    this.greetering = message;
  }
  // 在使用任何类成员是都使用this关键字
  greet() {
    return 'Hello, ' + this.greetering;
  }
}
let greeter = new Greeter('world');

继承

基于类的程序设计中,一种最基本的模式是允许使用继承来扩展现有的类

  • 类从基类中继承了属性和方法
  • 派生类手动包含构造函数时,必须调用super(),他会执行基类的构造函数,在构造函数里访问this的属性之前,必须要先调用super(),这是TS强制执行的重要规则
// 基类或者超类
class Animal {
  name: string;
  constructor(theName: string){
    this.name = theName;
  }
  move(distanceInMeters: number = 0){
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
// 派生类或者子类
class Snake extends Animal {
  constructor(name: string){
    super(name);
  }
  // 重写父类方法
  move(distanceInMeters = 5){
    console.log('Slithering...');
    super.move(distanceInMeters);
  }
}
class Horse extends Animal {
  constructor(name: string){
    super(name);
  }
  move(distanceInMeters = 45){
    console.log('Galloping...');
    super.move(distanceInMeters);
  }
}

let sam = new Snake('Sammy the Python');
// 即使定义成Animal类型,但是他的值是House,所以调用move时,会调用Horse里重写的方法
let tom: Animal = new Horse('Tommy the Palomino');

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

成员修饰符

  • public:定义类成员时如果不加修饰符那么默认为public,可以随意访问类中定义的成员
  • private:不能在声明他的类的外部访问
  • protected:与private相似,但是protected成员在派生类(子类)中仍然可以访问
  • readonly:将属性设置为只读的,只读属性必须在声明时或构造函数里被初始化
// public 类型 
class Person2 {
  public name: string;
  public constructor(theName: string){
    this.name = theName;
  }
  public move(distanceInMeters: number){
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
// private类型
class Person1 {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
let person = new Person1("Book");
person.name; // error name是私有的访问不到
// protected类型
class Person {
  protected name: string;
  constructor(name: string){
    this.name = name;
  }
}
class Employ 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 Employ('Howard', 'Sales');
console.log(howard.getElevatorPitch());
console.log(howard.name) // error protected 类型外部无法访问
// 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 = 'Ald'; // error name属性是只读的
console.log(dad.name)

注:构造函数也可以被标记为protected,意味着这个类不能在包含他的类外被实例化,但能被继承

// 构造函数也可以被标记为protected,意味着这个类不能在包含他的类外被实例化,但能被继承
class Person3 {
  protected name: string;
  protected constructor(theName: string){
    this.name = theName;
  }
}
class Employee3 extends Person3{
  private department: string;
  constructor(name: string, department: string){
    super(name);
    this.department = department;
  }
  public getElement(){
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
  }
}
let hward = new Employee3('Howrl',"Sales");
let john = new Person3('John'); // error Person3的构造函数是被保护的

描述:TS使用的是结构性类型系统,当比较两种不同类型时,并不在乎他们从何处而来,如果所有成员类型都是兼容的,就认为他们的类型是兼容的

class Animal1 {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
class Rhino extends Animal1 {
  constructor(){
    super('Rhino');
  }
}
class Employee {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
let animal = new Animal1('Goat');
let rhino = new Rhino();
let employee = new Employee('Bob');
// animal跟rhino共同继承自基类Animal1 所以出处是一样的,所以类型是兼容的
animal = rhino;
// employee是独立的虽然字段一样,但是出处不同,所以TS会认为类型不兼容
animal = employee; // error animal 与 employee不兼容

特殊:当比较带有private或protected成员的类型的时候,如果其中一个类型里包含一个private成员,那么只有当另外一个类型中也存在这样一个private成员,并且他们都来自同一处声明时,才认为这两个类是兼容的,protected同样适用

参数属性

可以方便的在一个地方定义并初始化一个成员
定义:参数属性通过给构造函数参数前面添加一个访问限定符来声明,使用private限定一个参数属性会声明并初始化成一个私有成员,public,protected类似

class Octopus1 {
  readonly numberOfLegs: number = 8;
  // 直接在构造函数中创建跟初始化name成员,把声明跟赋值合并
  constructor(readonly name: string){}
}

存取器

TS通过getters/setters来截取对对象成员的访问,可以有效控制对象成员访问
注意:

  • 存取器支持ES5或者更高版本
  • 只有带get不带set的存取器自动被推断为readonly
let passcode = 'secret passcode';
class Employee4{
  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 employee4 = new Employee4();
employee4.fullName = 'Bob Smith';
if(employee4.fullName){
  console.log(employee4.fullName);
}

静态属性

类实例成员:只有在类被实例化的时候才会被初始化,并且通过this.访问实例成员

静态属性:属性存在于类本身上而不是实例上,使用static关键字定义,通过构造函数名.来访问静态成员

class Grid {
  static origin = { x: 0, y: 0 };
  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;
  }
  constructor(public scale: number){}
}
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 Animal4{
  abstract makeSound(): void;
  move(): void {
    console.log('roaming the earch...');
  }
}

abstract class Department {
  constructor(public name: string){}
  printName(): void {
    console.log('Department name: ' + this.name);
  }
  abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
  constructor(){
    super('Accounting and Auditiing'); // 派生类构造函数必须调用super()
  }
  printMeeting(): void {
    console.log('The Accounting Department meets each Monday at 10am.');
  }
  generateReports(): void {
    console.log('Generating accounting reports...');
  }
}
let departMent: Department; // 允许创建一个对抽象类型的引用
// departMent = new Department(); // error 不能创建一个抽象类型的实例
departMent = new AccountingDepartment(); // 允许对一个抽象类子类进行实例化跟赋值
departMent.printName();
departMent.printMeeting();
// departMent.generateReports(); // error 方法在声明的抽象类中不存在

构造函数

当在TS中声明了一个类时,实际上同时声明了很多东西:

  • 实例的类型
  • 创建了一个构造函数,构造函数会在使用new创建类实例的时候被调用,这个构造函数也包含了类的所有静态属性
class Greeter1 {
  greeting: string;
  constructor(message: string){
    this.greeting = message;
  }
  greet() {
    return 'Hello, ' + this.greeting;
  }
}
// Greeter1类的实例的类型是Greeter1
let greete: Greeter1;
greete = new Greeter1('world');
console.log(greete.greet());

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

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

相关文章

网友评论

      本文标题:

      本文链接:https://www.haomeiwen.com/subject/vzvlyctx.html