类的基本结构
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
如上声明一个 Greeter
类。这个类有3个成员:一个叫做 greeting
的属性,一个构造函数和一个 greet
方法。
最后一行,使用 new
构造了 Greeter
类的一个实例。 它会调用之前定义的构造函数,创建一个 Greeter
类型的新对象,并执行构造函数初始化它。
继承
class A {
move(num:number=0){
console.log(num);
}
}
class B extends A{
back(){
console.log('123');
}
}
const b = new B()
b.back()//123
b.move(10)//10
类从基类中继承了属性和方法。 这里, B
是一个 派生类,它派生自 A
基类,通过 extends
关键字。 派生类通常被称作 子类,基类通常被称作 超类/父类。
重写
class A {
name:string;
constructor(theName:string) {
this.name = theName
}
move(num:number=0){
console.log(`${this.name}${num}`);
}
}
class B extends A{
constructor(name:string){
super(name)
}
move(num=5){
super.move(num)
}
}
class C extends A{
constructor(name:string){
super(name)
}
move(num=6){
super.move(num)
}
}
let b = new B('alibaba')
let c:A = new C('baidu')
b.move()
c.move(123)
输出
alibaba5
baidu123
简单来讲,我们创建了两个A的子类,B和C,B和C都重写了A中的move方法,使得 move
方法根据不同的类而具有不同的功能。
注意,即使 c
被声明为 A
类型,但因为它的值是 C
,调用 c.move(34)
时,它会调用 C里重写的方法
与前一个例子的不同点是,子类中要执行父类的构造函数必须在子类的构造函数中执行super()。 而且,在构造函数里访问 this
的属性之前,我们 一定要调用 super()
。 这个是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)
在父类外面无法被访问
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); // 错误,属性“name”受保护,只能在类“Person”及其子类中访问
构造函数也可以被标记成 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 是只读的.
存取器
支持通过getters/setters来截取对对象成员的访
这是一个简单类
静态成员(static)
静态属性和方法
class A{
static name:String = 'baidu'
age : number = 20;
static readyName (){
console.log('静态方法');
}
readyAge(){
console.log('实例方法');
}
}
A.name
A.age // 类型“typeof A”上不存在属性“age”
let a = new A()
a.readyName() // 属性“readyName”在类型“A”上不存在
a.readyAge()
静态属性和方法只存在与类的本身,外部无法调用
实例属性和方法只能通过类的实例来调用
抽象类(abstract)
举一个例子,飞机大战,面有好多飞机
,有敌方的飞机
,玩家飞机
等。那么根据类的划分,我们可以划分敌方飞机类
,玩家飞机类
,但是我们从继承
的角度仔细想一想,是不是有一些公共的部分,例如,飞机的名字
,飞机的生命值
,飞机的速度
,攻击
等一系列公共的属性和方法
我们先看一个普通类实现
class Plane{
name:string = '';
life:string = '';
attack(){
console.log(`{this.name}飞机正在攻击`)
}
}
class PlayerPlane extends Plane{
name:string = '玩家1';
life:string = '100';
// ...
attack(){
console.log(`{this.name}飞机正在攻击`);
// 飞机相同的逻辑1
// 飞机相同的逻辑2
// 飞机的不同逻辑
}
}
class EnemyPlane extends Plane{
name:string = '小罗罗1';
life:string = '10';
// ...
attack(){
console.log(`{this.name}飞机正在攻击`)
// 飞机相同的逻辑1
// 飞机相同的逻辑2
// 飞机的不同逻辑
}
}
我们创建了一个类,里面有属性和方法,把它当做飞机的模板
然后开始造飞机,每个飞机都有自己的属性和方法
可以看出,有些相同的逻辑每个子类都要写一遍
为了避免这个问题,我们可以用抽象类
abstract class Plane{
abstract name:string ;
abstract life:string ;
attack(){
console.log(`{this.name}飞机正在攻击`)
// 飞机相同的逻辑1
// 飞机相同的逻辑2
this.otherFunction();
}
// 飞机的不同逻辑
abstract otherFunction()
}
class PlayerPlane extends Plane{
name:string = '玩家1';
life:string = '100';
// ...
otherFunction(){
// ...个性化
}
}
class EnemyPlane extends Plane{
name:string = '小罗罗1';
life:string = '10';
// ...
otherFunction(){
// ...个性化
}
}
把所有相同的逻辑放到父类中去实现