TypeScript基础 -类

南山隐士 2022年06月25日 28 0

在typescript中定义类,跟es6相似,只是需要为属性定义类型

class Person {
  name:string;   // 定义实例属性类型
  constructor(n:string){
    this.name=n;
  }
  run():void{
    alert(this.name);
  }
}
console.log(Person)

上面的代码中定义了一个person类,并且定义一个name实例属性,如果在constructor中使用了某个实例属性,需要先定义类型, 这跟以往是不一样的

继承

在ES6中我们都知道继承,必须要在constructor中调用super, typescript中也是一样

class Person {
  name: string
  constructor (name: string) {
    this.name = name
  }
  work ():void {
    alert(`${this.name}在工作`)
  }
}

class Robot extends Person {
  constructor (name: string) {
    super(name) /*初始化父类的构造函数*/
  }
  work ():void {
    alert(`机器人${this.name}在工作`)
  }
}

let xiaoming = new Robot('小明')
xiaoming.work() 

上面的代码中, Robot类重写了Person的work方法,并且执行了基类(父类)的构造函数

类的修饰符

typescript里面定义属性的时候给我们提供了 ==四种修饰符==
public: ==公有==在当前类里面、 ==子类 、类外面都可以访问==
private :==私有==在当前类里面可以访问,==子类、类外部都没法访问==
protected:==保护==在当前类里面、子类里面可以访问 ,==在类外部没法访问==
readonly ==只读== 不能修改

public

class Animal {
  public name: string;
  public constructor(theName: string) { this.name = theName; }
  public move(distanceInMeters: number) {
      console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
let tiger = new Animal('老虎')
tiger.move(1)

像上面的代码,你没有显示的定义 public , ==typescript都会默认添加==, public的属性和方法, 我们都可以在类的外部进行使用

private

当成员被标记成 private时,它就不能在声明它的类的外部访问

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

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

让我们再来看看这段代码

class Work {
  private thing: string
  constructor (name: string) {
    this.thing = name
    console.log(this.thing) // 只能在class内部使用
  }
}

let coding = new Work('编码中')
// console.log(coding.thing) // error, private的属性不能外部访问
class diffWork {
  private thing: string
  constructor (name: string) {
    this.thing = name
    console.log(this.thing) // 只能在class内部使用
  }
}
let diffcoding = new diffWork('写框架中')
// coding = diffcoding // error 错误的, private的属性是私有的, 所以没办法赋值

class easyWrok extends Work {
  constructor() {
    super('写基础代码')
  }
}

let easyCoding = new easyWrok()
coding = easyCoding // 可以的, 因为继承的关系, 我们都共用了同一个private
console.log(coding)

可以看到当我们使用private 的时候, 哪怕我们同一个类中的private属性都一致,但是都无法将类的值赋值到其他的类中, 因为我们的private属性是私有的两个不一样, 而继承的却可以相关赋值改变, 因为我们都使用同一个private

protected

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。

class Animal {
  name: string;
  protected constructor(theName: string) { this.name = theName; }
  move(distanceInMeters: number) {
      console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
class Rabbit extends Animal {
  constructor (name: string) {
    super(name)
  }
}
let xiaobai = new Rabbit('小白')
xiaobai.move(1) // 小白 moved 1m.
let tiger = new Animal('老虎') // 报错了

上面的代码中, 可以看到在外部定义Animal类会报错的,因为 Animal的constructor属于保护类型,这个时候,==只能让子类使用或者自身使用==,可以看到Rabbit类可以成功创建实例属性

readonly

使用 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 Person {
  constructor(public name:string){}
  run():void{
    alert(this.name);
  }
}
let yaojin = new Person('遥近')
yaojin.run()

这样很方便的让我们初始属性并且定义修饰符

存取器

跟es5一样, TypeScript也支持通过getters/setters来截取对对象成员的访问

// 存取器
let verifyPass = '2018101238'
class accessorClass {
  private _name: string = 'Admin'
  get userName ():string {
    return this._name
  }

  set userName (otherUser){
    if (verifyPass && verifyPass === '20181018') {
      this._name = 'YaoJin'
    } else {
      this._name = otherUser
    }
  }
}

let login = new accessorClass()
console.log(login.userName)
login.userName = '啊哈哈哈' // 当verifyPass 为指定的值时候, 才会变成Yaojin, 其他将会按你改变的来
if (login.userName) {
  alert(login.userName)
}

可以看到, 上面通过set来截取了某个属性的存值行为, 限制你的值是否可以进行改动, 这个跟es6的set 和 get 是一样的行为, 需要注意的是 ==只带有 get不带有 set的存取器自动被推断为 readonly==

静态属性

class staticClass {
  static money: number = 9999
  tellMeYouMoney (name: string): void {
    console.log(`${name} 有 ${staticClass.money} 元`)
  }
}
class staticClass2 extends staticClass {
  tellMeYouMoney (name: string): void {
    console.log(`${name} 有 ${staticClass2.money} 元`)
  }
}
let YaoJin1 = new staticClass()
let YaoJin2 = new staticClass()

YaoJin1.tellMeYouMoney('遥近1')
YaoJin1.tellMeYouMoney('遥近2')

let YaoJin3 = new staticClass2()
YaoJin3.tellMeYouMoney('遥近3')

静态属性和方法只能在类中使用, 并不能在类的实例中使用,一些公共的方法和属性, 我们可以考虑放到这里

抽象类

抽象类做为其它派生类 的 基类使用, 可以理解为抽象类也是一个父类, 可以使用 ==abstract关键字== 定义 抽象类 和在 抽象类内部定义抽象方法, 需要注意的是 抽象类中的抽象方法==不包含具体实现并且必须在派生类中实现==, 也就是说在抽象类中定义的抽象方法不需要在里面写具体的逻辑, 具体实现在子类中体现,并且子类必须使用这个抽象方法, ==抽象类不可以创建实例哦==

abstract class abstractClass {
  abstract sayName(name:string): void; // 必须在派生类中实现
}

class sayNameClass extends abstractClass {
  sayName(name:string): void { // 必须在派生类中使用它
    console.log(name)
  }
  otherFun ():void {
    console.log("不能使用") // 如果抽象类中没有定义这个方法, 子类也没法使用
  }
}
let say: abstractClass
say = new sayNameClass()
// let abstract = new abstractClass() // 错误, 抽象类不能创建实例
say.sayName('YaoJin')
/* 
错误, 因为你将say的类型定义abstractClass, abstractClass 中不存在该方法, 
如果想不报错, 那么就不将其类型定义为abstractClass 
*/
// say.otherFun()

高级技巧

当我们创建一个类的时候, 会创建的类的实例类型和一个构造函数, 如果我们想将这个类的静态属性和构造函数, 赋值到一个变量中, 可以使用 typeof

let greeterMaker: typeof Greeter = Greeter;

像这样, greeterMaker 就等于一个构造函数, 可以创建实例,并且有Greeter 的静态属性

把类当做接口使用

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

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

类也可以作为接口使用, 让某个接口继承这个类, 这个接口就多了一些类型的检查定义

Last Updated: 2022/06/25 19:14:41
TypeScript基础-函数 TypeScript基础-接口