教程4:类与继承

141 阅读4分钟

前情提要

# 教程1:花费一分钟,跟着做,傻瓜式入门Typescript

# 教程2:Typescript的变量类型介绍

# 教程3:Typescript的类型别名及接口

可以理解为创建对象的模型

语法:

  1. 使用class 类名{}来定义类,类名以大写字母开头
  2. 通过new 类名()生成一个实例对象

实例属性

class Person {
  // public修饰的属性为实例属性(默认修饰符,可省略)
  // 定义类的实例对象属性,通过实例对象操作
  public name: string = 'lemon'
  public age:number =  17
}

let lemon = new Person()
lemon.name // 获取实例对象属性name
lemon.age = 18 // 修改实例对象属性age

静态属性

class FemalePerson {
   // static开头的属性为静态属性(类属性), 只能通过类去获取
   static gender: string = '女'
}
​
let lemon = new FemalePerson()
FemalePerson.gender // 通过类名获取静态属性

实例方法或静态方法中的this

class Person {
  public introduce () {
    // 实例方法里面的this指向该实例对象
    console.log(this)
  }
  
  static sayHi () {
    // 静态方法里面的this指向这个类
    console.log(this)
  }
}
​
let lemon = new Person()
lemon.introduce() // 调用实例对象方法
Person.sayHi() // 调用静态方法

构造函数

  • 构造函数会在对象创建时被调用
  • 构造函数的this指向实例对象
class Person {
    public name: string
    public age:number
    // 构造函数会在对象创建时被调用
    constructor(name: string, age: number) {
        console.log(this) // 构造函数的this指向实例对象
        this.name = name
        this.age = age
    }
}
​
let lemon = new Person('lemon', 17)
lemon.name // 获取实例对象的name属性值

当实例属性与构造函数形参名一致时,可以简写成:

class Person {
    constructor(public name: string, public age: number) {
        console.log(this)
        this.name = name
        this.age = age
    }
}

继承

语法: class 子类名 extends 父类{}

使用继承后,子类将拥有父类的所有属性及方法

class Animal {
    static name = '动物'
    public sayHi () {
        console.log('动物在叫')
    }
}
class Dog extends Animal {
    // 子类在父类的基础上扩展方法
    public eat () {
        console.log('吃骨头')
    }
}
​
let wangcai = new Dog()
Dog.name // 打印: '动物'
wangcai.sayHi() // 继承父类的方法,打印: '动物在叫'
wangcai.eat() // 打印: '吃骨头'

方法重写

class Animal {
    public sayHi () {
        console.log('动物在叫')
    }
}
class Dog extends Animal {
    // 子类重新定义父类已存在的方法,称为方法重写
    public sayHi () {
        console.log('汪汪汪')
    }
}
let wangcai = new Dog()
wangcai.sayHi() // 继承父类的方法,打印: '汪汪汪'

super关键字

使用super关键字继承父类的实例属性

class Animal {
    constructor (public name: string) {
        this.name = name
    }
}
// 错误写法:
class Dog extends Animal {
    // 试想:这里是不是也属于方法重写呢? 那么是不是Animal的实例属性name就被覆盖了呢?
    // 所以这里是不允许这么去书写的
    constructor (public age: number) {
        this.age = age
    }
}
​
// 正确写法:
class Dog extends Animal {
    constructor (public name: string, public age: number) {
        // 调用 super()执行父类的构造函数,从而实现继承父类的实例属性
        super(name)
        this.age = age
    }
}

其他修饰符

readonly

class FemalePerson {
  // readonly开头的属性为只读实例属性,表示只能获取,不能修改
  readonly name: string = 'lemon'
}
​
let lemon = new FemalePerson()
lemon.name = 'laoxie' // 报错,该属性只读,不可修改

public VS private VS protected

  1. public 修饰的属性可以在任意位置获取或修改

  2. private 私有属性,只能在类内部进行获取或修改

    • 通过在类中添加方法,使得私有属性能被获取或修改
class Animal {
    constructor(private name: string) {
        this.name = name;
    }
}
​
class Dog extends Animal {
    constructor(public name: string) {
        super(name);
        console.log(this.name); // 报错:Property 'name' is private and only accessible within class 'Animal'
    }
}

通过getter&setter使用私有属性

class FemalePerson {
  private _age: number
  constructor (age: number) {
    this._age = age
  }
  set age (val: number) {
    if (val >= 0) {
        this._age = val
    }
  }
  get age () {
    return this._age
  }
}
​
let lemon = new FemalePerson(18)
lemon.age // 打印: 18
lemon.age = -20 // 不执行修改
lemon.age = 20 // 成功修改
  1. protected 受保护的属性,只能在类中或其子类中获取或修改

(介于public与private之间)

class Animal {
    constructor(protected name: string) {
        this.name = name;
    }
}
​
class Dog extends Animal {
    constructor(public name: string) {
        super(name);
        console.log(this.name); // 可以获取
    }
}

抽象类

语法: abstract class 类名{}

  • 抽象类不能用来创建对象
  • 抽象类是专门用来被继承的类
abstract class Animal {
    constructor (public name: string) {
        this.name = name
    }
    // 抽象方法用abstract修饰开头,没有方法体
    // 抽象方法只能定义在抽象类中,子类必须对抽象方法重写
    abstract sayHi(): void
}

类与接口

类实现接口

有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。

interface Alarm {
    alert(): void;
}
​
class SecurityDoor implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}
​
class SecurityCar implements Alarm {
    alert() {
        console.log('Car alert');
    }
}

一个类可以实现多个接口:

interface Alarm {
    alert(): void;
}
​
interface Light {
    lightOn(): void;
    lightOff(): void;
}
​
// Car 实现了 Alarm 和 Light 接口,既能报警,也能开关车灯
class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}

接口继承接口

接口与接口之间可以是继承关系:

interface Alarm {
    alert(): void;
}
​
// LightableAlarm 继承了 Alarm,除了拥有 alert 方法之外,还拥有两个新方法 lightOn 和 lightOff。
interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}

接口继承类

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}
​
interface Point3d extends Point {
    z: number;
}

扩展

当我们在声明class Greeter时,除了创建一个名为Greeter的类之外,同时也创建了一个变量类型Greeter

class Greeter {
    greet() {
        return "Hello, world"
    }
}
​
let greeter: Greeter = new Greeter("world") // Greeter类的实例对象的类型是Greeter
console.log(greeter.greet());

辛勤的前端园丁,立志于把每个知识点嚼碎了喂你嘴里!

下篇文章:函数与泛型,马不停蹄整理中...