TS-中篇: 面向对象

123 阅读5分钟

黑缎缠目

梦想使你迷醉,距离就成了欢乐;追求使你充实,失败和成功都是伴奏;当生命以美的形式证明其价值的时候,幸福是享受,痛苦也是享受。 ———史铁生

TS中的类

定义一个类

注意的是相较于js, ts定义一个类还需要声明构造方法中的成员变量

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

语法糖写法

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

成员修饰符

public: 公有的, 在任何地方都可访问

private: 私有的, 只能在本类的内部进行使用, 私有化成员变量命名习惯使用下短线作为前缀: private _name: string

protected: 受保护的, 只能在本类和该类的子类中进行访问

关键字readonly

readonly 修饰的属性只能在初始化时赋值,并且赋值后不能再次修改。

readonly name: stirng

类中的存取器-getter/setter

通过getter/setter存取器, 外部实例可以对类中使用private修饰的变量方法进行间接操作

class Person {
    private _name: string
    private _age: number
    constructor(name: string, age: number)  {
      this._name = name
      this._age = age
    }

    set name(newValue: string) {
      this._name = newValue
    }
    get name() {
      return this._name
    }
  }
  const p = new Person("messi", 111)
  p.name = "kobe"

抽象类

抽象类是一种不能直接实例化的类,它只能被用作其他类的父类。抽象类可以包含抽象方法和非抽象方法。而抽象方法只能定义在抽象类中。抽象方法必须在子类中被实现,而非抽象方法可以被子类继承或重写。

abstract class Shape {
  abstract getArea()
  }
class rectangle extends Shape {
  constructor(public length: number, public width: number) {
    super()
  }
  getArea() {
    return this.length * this.width
  }
}
function getArea(shape: Shape) {
  return shape.getArea()
}
getArea(new rectangle(1, 5))

类的继承

和js中差不多可以类比, 使用extends关键字来实现继承,子类中使用super来访问父类。继承可减少代码量同时也是多态的基础。

对象类型的属性修饰符

只读属性-可选属性

interface Iobj {
    readonly name: string
    age?: number
  }

接口的继承

接口是支持多继承的

interface Person {
  name: string
  age: number
}
interface Sex {
  sex: string
}
interface Student extends Person, Sex {
  id: number
}
const stuObj: Student = {
  name: "小李子",
  age: 18,
  id: 21012280
  sex: "男"
}

接口的实现

类实现接口时必须要实现接口中所有的属性以及方法, 并且类可以实现多个接口。

ts中类实现接口的好处:

  • 更强的约束性:接口定义了一组规范,而类实现了这个规范。因此,当一个类实现了接口时,它必须符合接口中定义的所有属性和方法规范,从而提高了代码的约束性。这有助于防止出现意外的错误或者漏掉某些功能。

  • 更强的可替换性:如果一个类实现了某个接口,那么该类的实例就可以被当作接口类型来使用。这意味着,我们可以在不改变代码结构的情况下,替换具体的实现类。这样,我们可以更容易地修改程序的行为或适应变化的需求,提高了代码的可维护性和可扩展性。

  • 更好的可读性和可理解性:接口提供了更加明确和整洁的代码结构,使得代码更易于阅读和理解。实现类与接口之间的映射关系也更加直观和清晰,从而更方便团队协作和代码审核。

  interface IPerson {
    name: string
    age: number
    eating: () => void
  }
  interface IActive {
    running: () => void
    learning: () => void
  }

  class Student implements IPerson, IActive{
    name: string = "kobe"
    age: number = 33
    eating() {}
    running() {}
    learning() {}
  }

  const p = new Student()
  p.name

索要签名

可以使用索引签名(Index Signatures)来描述一个对象/数组的属性类型值类型也就是key类型和value类型。它允许我们定义一组动态的属性,注意: key类型必须为string类型 或者是number类型

下面的例子表示声明一个key为stirng类型, value为string类型的对象类型

interface IPerson {
  [key: number]: string
}

通过索引访问的类型问题一

索引类型为number, 但是为什么可以通过string类型的索引去访问数组呢? 这对于数组和对象都适用。

现象解释: 通过number类型的索引去访问数组内容的时候, 最终在解析的时候js会把number类型索引转化为string类型。注意:这里的string类型只能是对应的number类型的转化的string类型, 如数字0转化为字符串"0"

interface IPerson {
  [index: number]: string
}
const names: IPerson = [ "abc", "nba", "cba" ]
console.log(names[0])
console.log(names["0"])

const obj: IPerson = {
  1: "messi"
}
console.log(obj["1"])
console.log(obj[1])

通过索引访问的类型问题二

针对数组, 当索引类型是string时, 返回值类型不能是string。

现象解答: 因为通过字符串索引去访问数组实例可能会访问到数组实例中的其他属性例如数组中的forEach方法, 而它的类型是一个函数类型。所以索引签名返回值不能只是string类型, 而通过number类型索引访问的话就只是访问数组中的某一项, 而不是Array实例对象中的方法, 所以放回值可以是string类型

图片.png

双索引签名类型问题

描述一下这里的问题, 双索引标签中, 数字索引的返回类型一定要是字符索引返回类型的子类型。 图片.png

图片.png