Typescript 从入门到放弃系列(五)-类

210 阅读3分钟

一.TS 中定义类

class Pointer {
  x!: number; // 实例上的属性必须先声明
  y!: number;
  constructor(x: number, y?: number, ...args: number[]) {
    this.x = x;
    this.y = y as number;
  }
}
let p = new Pointer(100, 200);

实例上的属性需要先声明在使用,构造函数中的参数可以使用可选参数和剩余参数

二.类中的修饰符

  • public修饰符(谁都可以访问到)
class Animal {
  public name!: string; // 不写public默认也是公开的
  public age!: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
class Cat extends Animal {
  constructor(name: string, age: number) {
    super(name, age);
    console.log(this.name, this.age); // 子类访问
  }
}
let p = new Cat("Tom", 18);
console.log(p.name, p.age); // 外层访问
class Animal {
  constructor(public name: string, public age: number) {
    this.name = name;
    this.age = age;
  }
}

我们可以通过参数属性来简化父类中的代码

  • protected修饰符 (自己和子类可以访问到)
class Animal {
  constructor(protected name: string, protected age: number) {
    this.name = name;
    this.age = age;
  }
}
class Cat extends Animal {
  constructor(name: string, age: number) {
    super(name, age);
    console.log(this.name, this.age);
  }
}
let p = new Cat("Tom", 18);
console.log(p.name, p.age); // 无法访问
  • private修饰符 (除了自己都访问不到)
class Animal {
  constructor(private name: string, private age: number) {
    this.name = name;
    this.age = age;
  }
}
class Cat extends Animal {
  constructor(name: string, age: number) {
    super(name, age);
    console.log(this.name, this.age); // 无法访问
  }
}
let p = new Cat("Tom", 18);
console.log(p.name, p.age); // 无法访问
  • readonly修饰符 (仅读修饰符)
class Animal {
  constructor(public readonly name: string, public age: number) {
    this.name = name;
    this.age = age;
  }
  changeName(name: string) {
    this.name = name; // 仅读属性只能在constructor中被赋值
  }
}
class Cat extends Animal {
  constructor(name: string, age: number) {
    super(name, age);
  }
}
let p = new Cat("Tom", 18);
p.changeName("Jerry");

三.静态属性和方法

class Animal {
  static type = "哺乳动物"; // 静态属性
  static getName() {
    // 静态方法
    return "动物类";
  }
  private _name: string = "Tom";

  get name() {
    // 属性访问器
    return this._name;
  }
  set name(name: string) {
    this._name = name;
  }
}
let animal = new Animal();
console.log(animal.name);

静态属性和静态方法是可以被子类所继承的

四.Super 属性

class Animal {
  say(message: string) {
    console.log(message);
  }
  static getType() {
    return "动物";
  }
}
class Cat extends Animal {
  say() {
    // 原型方法中的super指代的是父类的原型
    super.say("猫猫叫");
  }
  static getType() {
    // 静态方法中的super指代的是父类
    return super.getType();
  }
}
let cat = new Cat();
console.log(Cat.getType());

五.类的装饰器

1.装饰类

function addSay(target: any) {
  target.prototype.say = function () {
    console.log("say");
  };
}
@addSay
class Person {
  say!: Function;
}
let person = new Person();
person.say();

装饰类可以给类扩展功能,需要开启experimentalDecorators:true

2.装饰类中属性

function toUpperCase(target: any, key: string) {
  let value = target[key];
  Object.defineProperty(target, key, {
    get() {
      return value.toUpperCase();
    },
    set(newValue) {
      value = newValue;
    },
  });
}
function double(target: any, key: string) {
  let value = target[key];
  Object.defineProperty(target, key, {
    get() {
      return value * 2;
    },
    set(newValue) {
      value = newValue;
    },
  });
}
class Person {
  @toUpperCase
  name: string = "James";
  @double
  static age: number = 18;
  getName() {
    return this.name;
  }
}
let person = new Person();
console.log(person.getName(), Person.age);

装饰属性可以对属性的内容进行改写,装饰的是实例属性则 target 指向类的原型、装饰的是静态属性则 target 执行类本身~

3.装饰类中方法

function noEnum(target: any, key: string, descriptor: PropertyDescriptor) {
  console.log(descriptor);
  descriptor.enumerable = false;
}
class Person {
  @toUpperCase
  name: string = "James";
  @double
  static age: number = 18;
  @noEnum
  getName() {
    return this.name;
  }
}
let person = new Person();
console.log(person); // getName 不可枚举

4.装饰参数

function addPrefix(target: any, key: string, paramIndex: number) {
  console.log(target, key, paramIndex); // Person.prototype getName  0
}
class Person {
  @toUpperCase
  name: string = "James";
  @double
  static age: number = 18;
  prefix!: string;
  @noEnum
  getName(@addPrefix prefix: string) {
    return this.name;
  }
}

六.抽象类

抽象类无法被实例化,只能被继承,抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现。

abstract class Animal {
  name!: string;
  abstract speak(): void;
}
class Cat extends Animal {
  speak() {
    console.log("猫猫叫");
  }
}
class Dog extends Animal {
  speak(): string {
    console.log("汪汪叫");
    return "wangwang";
  }
}

定义类型时void表示函数的返回值为空(不关心返回值类型,所有在定义函数时也不关心函数返回值类型)