乐趣区

TypeScript自学第四章类

下面请看类的使用

class Greeter {
  greeting: string;
  constructor(message: string) {this.greeting = message;}
  greet() {return "Hello," + this.greeting;}
}

let greeter = new Greeter("world");

有三个部分一个叫做 greeting的属性,一个构造函数和一个 greet方法。

继承

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(distanceInMeters = 5){console.log("Slithering...")
    super.move(distanceInMeters) // 调用父类的 move 方法
  }
}

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")
let tom:Animal = new Horse("Tommy the Palomino")
sam.move()
tom.move(34)

派生类包含了一个构造函数,它必须调用 super(),它会执行基类的构造函数。
如果在要构造器里访问 this 属性,必须先调用 super()
同时,这个例子还重写了 move 方法

// 打印结果
Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.

公共,私有与受保护的修饰符

public

在 TypeScript 里,成员都默认为 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(theName: string) {this.name = theName;}
}   

new Animal("Cat").name; // 错误: 'name' 是私有的.
protected

protected修饰符与 private修饰符的行为很相似,但是 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。那么这个类可以被继承,但不能被实例化。

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

// Employee 能够继承 Person
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");
let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.

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 是只读的.

参数属性

将上一段运用参数属性来修改。如下:

class Octopus {
  readonly numberOfLegs: number = 8;
  constructor(readonly name: string) {}}

参数属性通过给构造函数参数前面添加一个访问限定符来声明。使用 private限定一个参数属性会声明并初始化一个私有成员;对于 publicprotected来说也是一样。

存取器

无存取器的代码

class Employee {fullName: string;}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {console.log(employee.fullName);
}

set方法与 get 方法

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);
}

注意只带有 get不带有 set的存取器自动被推断为 readonly

静态属性

静态属性只存在类身上,而不在类的实例上。

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); // 1x scale
let grid2 = new Grid(5.0); // 5x scale

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

使用 static定义 origin,因为它是所有网格都会用到的属性。每个实例想要访问这个属性的时候,都要在 origin前面加上类名。即 Grid. 来访问静态属性。

抽象类

抽象类做为其它派生类的基类使用。它们一般不会直接被实例化。

abstract class Animal {abstract makeSound(): void;
  move(): void {console.log("roaming the earch...");
  }
}

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。抽象方法的语法与接口方法相似。两者都是定义方法签名但不包含方法体。然而,抽象方法必须包含 abstract关键字并且可以包含访问修饰符。

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 Auditing"); // 在派生类的构造函数中必须调用 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(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
department.generateReports(); // 错误: 方法在声明的抽象类中不存在

高级技巧

构造函数
// ts 写法
class Greeter {
  greeting: string;
  constructor(message: string) {this.greeting = message;}
  greet() {return 'Hello,' + this.greeting;}
}
let greeter: Greeter;
greeter = new Greeter('world');
console.log(greeter.greet());
// 上一段 转为 JS 写法
let Greeter = (function() {function Greeter(message) {this.greeting = message;}
  Greeter.prototype.greet = function() {return 'Hello,' + this.greeting;};
  return Greeter;
})();

let greeter;
greeter = new Greeter('world');
console.log(greeter.greet());

我们可以认为类具有 实例部分 静态部分 这两个部分。
让我们稍微改写一下这个例子,看看它们之间的区别:

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());

把类当做接口使用

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

interface Point3d extends Point {z: number;}

let point3d: Point3d = {x: 1, y: 2, z: 3};
退出移动版