类的声明 & 实现

TS为成员属性和构造函数的参数添加了类型注解 run方法默认返回值是void

  1. class Dog {
  2. constructor(name: string) {
  3. this.name = name;
  4. }
  5. name: string;
  6. run() {}
  7. }

image.png
构造函数的返回值类型默认会被推断为Dog,即本类
image.png

注:

无论是在ES还是TS中,类的成员属性都是实例属性(而不是原型属性),类的成员方法都是原型方法

打印类的原型,查看是否包含内部属性

  1. console.log(Dog.prototype);

image.png
类的原型不包含内部属性,原型上只有 run 方法和 constructor 构造方法

创建一个实例,并且输出

  1. class Dog {
  2. constructor(name: string) {
  3. this.name = name;
  4. }
  5. name: string;
  6. run() {}
  7. }
  8. console.log(Dog.prototype);
  9. let dog = new Dog("wangcai");
  10. console.log(dog);

image.png
可以看到,内部属性 name 只在实例上,而不在原型上

异同点:

  • 在TS实例的属性必须有初始值,或在构造函数中被初始化
  • 如删掉构造函数中对name的初始化,将提示错误
  • 如下代码所示: ```typescript class Dog { constructor(name: string) {
    1. // this.name = name;
    } name: string; run() {} }

console.log(Dog.prototype);

let dog = new Dog(“wangcai”); console.log(dog);

  1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1544252/1596806211632-21995198-16e6-42b6-97a0-9a8c0ca200b1.png#align=left&display=inline&height=255&margin=%5Bobject%20Object%5D&name=image.png&originHeight=255&originWidth=467&size=34980&status=done&style=shadow&width=467)<br />处理:
  2. ```typescript
  3. // 初始值
  4. {
  5. class Dog {
  6. constructor(name: string) {
  7. // this.name = name;
  8. }
  9. name: string = "Dog";
  10. run() {}
  11. }
  12. console.log(Dog.prototype);
  13. let dog = new Dog("wangcai");
  14. console.log(dog);
  15. }
  16. // 可选属性
  17. {
  18. class Dog {
  19. constructor(name: string) {
  20. // this.name = name;
  21. }
  22. name?: string;
  23. run() {}
  24. }
  25. console.log(Dog.prototype);
  26. let dog = new Dog("wangcai");
  27. console.log(dog);
  28. }

总结:只要内部属性有初始值,就不会报错

继承

类的继承使用了extends关键字 类的构造函数constructor中调用了super,代表父类实例 父类Dog的构造函数中有一个参数name,所以子类也必须要有具有此参数

  1. // 继承
  2. class Husky extends Dog {
  3. constructor(name: string, color: string) {
  4. super(name);
  5. this.color = color;
  6. }
  7. color: string;
  8. }

成员修饰符

类的成员修饰符部分是TS对ES所做的扩展

公有成员-public

类的所有属性默认均为public,表示对所有人可见

  1. class Dog {
  2. constructor(name: string) {
  3. this.name = name;
  4. }
  5. public name: string;
  6. run() {}
  7. }

私有成员-private

私有成员,只能在类的本省调用,而不能被类的实例调用,也不能被子类调用

  1. class Dog {
  2. constructor(name: string) {
  3. this.name = name;
  4. }
  5. public name: string;
  6. // name: string = 'dog'
  7. run() {}
  8. // 私有成员,只能在类的本省调用,而不能被类的实例调用,也不能被子类调用
  9. private pri() {}
  10. }
  11. let dog = new Dog("wangwang");
  12. console.log(dog); // Dog {legs: 4, name: "wangwang"}
  13. dog.pri();

image.png
私有成员也不能在子类中被调用
image.png
类的构造函数也可以被设置为私有成员,既不能被实例化也不能被继承了

  1. // 类
  2. class Dog {
  3. private constructor(name: string) {
  4. this.name = name;
  5. }
  6. public name: string;
  7. run() {}
  8. private pri() {}
  9. }
  10. let dog = new Dog("wangwang");
  11. class Husky extends Dog {
  12. constructor(name: string, public color: string) {
  13. super(name);
  14. this.color = color;
  15. }
  16. }

image.png

受保护成员-protected

受保护成员,只能在类或者子类访问与调用,而不能在类的实例访问

  1. // 类
  2. class Dog {
  3. constructor(name: string) {
  4. this.name = name;
  5. }
  6. public name: string;
  7. run() {}
  8. private pri() {}
  9. protected pro() {}
  10. }
  11. let dog = new Dog("wangwang");
  12. dog.pro();
  13. // 继承
  14. class Husky extends Dog {
  15. constructor(name: string, public color: string) {
  16. super(name);
  17. this.color = color;
  18. this.pro();
  19. }
  20. // color: string;
  21. }

image.png
构造函数也可以被声明为 protected,使这个类只能被继承,不能被实例化,相当于声明了一个基类

  1. // 类
  2. class Dog {
  3. protected constructor(name: string) {
  4. this.name = name;
  5. }
  6. public name: string;
  7. run() {}
  8. private pri() {}
  9. protected pro() {}
  10. }
  11. let dog = new Dog("wangwang");
  12. // 继承
  13. class Husky extends Dog {
  14. constructor(name: string, public color: string) {
  15. super(name);
  16. this.color = color;
  17. this.pro();
  18. }
  19. // color: string;
  20. }

image.png

只读属性-protected

只读属性,这个属性不能被更改,而且不能被更改 和实例属性一样,类的只读属性必须被初始化

  1. // 类
  2. class Dog {
  3. constructor(name: string) {
  4. this.name = name;
  5. }
  6. public name: string;
  7. run() {}
  8. private pri() {}
  9. protected pro() {}
  10. readonly legs: number = 4;
  11. }
  12. let dog = new Dog("wangwang");
  13. console.log(dog); // Dog {legs: 4, name: "wangwang"}

静态成员-static

类的静态成员只能通过类名来调用,不能通过子类来调用 类的静态成员可以被继承

  1. // 类
  2. class Dog {
  3. constructor(name: string) {
  4. this.name = name;
  5. }
  6. public name: string;
  7. run() {}
  8. private pri() {}
  9. protected pro() {}
  10. readonly legs: number = 4;
  11. static food: string = "bones";
  12. }
  13. let dog = new Dog("wangwang");
  14. console.log(dog.food);
  15. console.log(Dog.food); // bones
  16. // 继承
  17. class Husky extends Dog {
  18. constructor(name: string, public color: string) {
  19. super(name);
  20. this.color = color;
  21. this.pro();
  22. }
  23. }
  24. console.log(Husky.food); // bones

image.png

构造函数的参数添加修饰符

除了类的成员可以添加修饰符外,构造函数的参数也可以添加修饰符 作用:将参数自动变成实例的属性,这样就可以省略在类中的定义

  1. // 类
  2. class Dog {
  3. constructor(name: string) {
  4. this.name = name;
  5. }
  6. public name: string;
  7. run() {}
  8. private pri() {}
  9. protected pro() {}
  10. readonly legs: number = 4;
  11. static food: string = "bones";
  12. }
  13. // 继承
  14. class Husky extends Dog {
  15. constructor(name: string, public color: string) {
  16. super(name);
  17. this.color = color;
  18. this.pro();
  19. }
  20. color: string;
  21. }

image.png

抽象类的定义

抽象类只能被继承,不能被实例化 定义抽象类使用abstract关键字

  1. abstract class Animal { }
  2. let animal = new Animal();

image.png

抽象类的继承

继承抽象类,需要在子类中调用super方法

  1. abstract class Animal {
  2. eat() {
  3. console.log("eat");
  4. }
  5. }
  6. class Dog extends Animal {
  7. constructor(name: string) {
  8. super();
  9. this.name = name;
  10. }
  11. name: string;
  12. run() {}
  13. }
  14. let dog = new Dog("wangwang");
  15. console.log(dog.name);

抽象类实现方法复用

在抽象类中可以定义方法(可以有具体实现,也可以是抽象方法),从而实现复用 抽象类可以抽离事物的共性,有利于代码的复用和扩展

  1. abstract class Animal {
  2. eat() {
  3. console.log("eat");
  4. }
  5. abstract sleep(): void;
  6. }
  7. class Dog extends Animal {
  8. constructor(name: string) {
  9. super();
  10. this.name = name;
  11. }
  12. name: string;
  13. sleep() {
  14. console.log("Dog sleep");
  15. }
  16. }
  17. let dog = new Dog("wangwang");
  18. dog.eat();
  19. dog.sleep();
  20. class Cat extends Animal {
  21. sleep() {
  22. console.log("Cat sleep");
  23. }
  24. }
  25. let cat = new Cat();
  26. cat.eat();
  27. cat.sleep();

抽象类实现多态

通过在父类中定义抽象方法,在不同子类中对该方法进行不同实现, 从而在运行时根据不同子类对象表现为不同状态

  1. abstract class Animal {
  2. eat() {
  3. console.log("eat");
  4. }
  5. abstract sleep(): void;
  6. }
  7. class Dog extends Animal {
  8. constructor(name: string) {
  9. super();
  10. this.name = name;
  11. }
  12. name: string;
  13. sleep() {
  14. console.log("Dog sleep");
  15. }
  16. }
  17. let dog = new Dog("wangwang");
  18. dog.eat();
  19. dog.sleep();
  20. class Cat extends Animal {
  21. sleep() {
  22. console.log("Cat sleep");
  23. }
  24. }
  25. let cat = new Cat();
  26. cat.eat();
  27. cat.sleep();
  28. let animal: Animal[] = [dog, cat];
  29. animal.forEach((i) => {
  30. i.sleep();
  31. });

this类型

类的成员方法可以直接返回一个this,方便实现链式调用,即Builder模式

  1. class WorkFlow {
  2. step1() {
  3. return this;
  4. }
  5. step2() {
  6. return this;
  7. }
  8. }
  9. new WorkFlow().step1().step2();

使用this实现多态

this代表实例本身,继承时,子类实例即可表现多态(this既可以是父类,也可以是子类)

  1. class WorkFlow {
  2. step1() {
  3. return this;
  4. }
  5. step2() {
  6. return this;
  7. }
  8. }
  9. new WorkFlow().step1().step2();
  10. class Myflow extends WorkFlow {
  11. next() {
  12. return this;
  13. }
  14. }
  15. new Myflow().next().step1().next().step2();

接口对类的约束作用

接口能够对类的成员属性和类型进行约束(且只能约束类的公有成员) 注意: 类必须实现接口中声明的全部属性和方法 类可以新增独有的属性和方法

  1. interface Human {
  2. // new(name: string): void
  3. name: string;
  4. eat(): void;
  5. }
  6. // 类实现接口的时候,必须实现接口所有声明的属性
  7. // 接口只能约束类的共有成员
  8. // 接口不能约束构造函数
  9. class Asion implements Human {
  10. constructor(name: string) {
  11. this.name = name;
  12. }
  13. name: string;
  14. eat() {}
  15. sleep() {}
  16. }

image.png

接口继承

接口和类一样,可以相互继承,且一个接口可以继承多个接口 接口的继承特性可以将可重用部分进行抽离,也可以将多个接口合并成一个接口

  1. interface Human {
  2. // new(name: string): void
  3. name: string;
  4. eat(): void;
  5. }
  6. // 类实现接口的时候,必须实现接口所有声明的属性
  7. // 接口只能约束类的共有成员
  8. // 接口不能约束构造函数
  9. class Asion implements Human {
  10. constructor(name: string) {
  11. this.name = name;
  12. }
  13. name: string;
  14. eat() {}
  15. sleep() {}
  16. }
  17. // 接口继承
  18. interface Man extends Human {
  19. run(): void;
  20. }
  21. // 具有另外行为的接口
  22. interface Child {
  23. cry(): void;
  24. }
  25. // 多个接口组合使用
  26. interface Boy extends Man, Child {}
  27. // 属性与方法
  28. let boy: Boy = {
  29. name: "",
  30. run() {},
  31. eat() {},
  32. cry() {},
  33. };

接口继承类

接口除了可以继承接口外,还可以继承类,相当于将类成员全部抽象出来,仅有类的结构而没有具体实现 注意: 接口抽离类成员时,不仅抽离了公共成员,也抽离了私有成员和受保护成员

  1. class Auto {
  2. state = 1;
  3. // private state2 = 0;
  4. }
  5. interface AutoInterface extends Auto {}
  6. class C implements AutoInterface {
  7. state = 1;
  8. }

image.png
image.png
image.png

  1. class Auto {
  2. state = 1;
  3. // private state2 = 0;
  4. protected state3 = 3;
  5. }
  6. interface AutoInterface extends Auto {}
  7. class C implements AutoInterface {
  8. state = 1;
  9. // private state2 = 2;
  10. protected state3 = 3;
  11. }
  12. class Bus extends Auto implements AutoInterface {}

学习笔记出自于梁宵老师课程