# 3. 类

ts除了实现所有ES6中的类功能外,还添加了一些新的方法

# 类(class)相关概念

  • 类(class):对事物的抽象,包含属性和方法
  • 对象(Object):类的实例,通过 new 生成
  • 面向对象的三大特性:封装、继承、多态
    • 封装(Encapsulation):将对数据操作细节隐藏,只暴露对外接口。只能通过暴露的接口来访问对象,而不能任意更改对象内部的数据
    • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
    • 多态(Polymorphism):有了继承才有多态,在调用类实例方法的时候,尽量把变量视作父类类型,这样,所有子类类型都可以正常被接收
  • setter或getter:用来改变属性的读取和赋值行为
  • 修饰符:修饰符是一些关键字,用于限定成员或类的性质,如 public 表示公有属性或方法
  • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现。
  • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象为一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但可以实现多个接口。

# ES6+中类的用法

// 属性和方法
class Animal {
  constructor(name) {
    this.name = name
  }
  sayHi() {
    return `My name is ${this.name}`
  }
}

// 类的继承
class Cat extends Animal {
  constructor(name) {
    super(name) // 调用父类的 constructor(name)
    console.log(this.name)
  }
  sayHi() {
    return `Cat, ` + super.sayHi() // 调用父类的sayHi
  }
}
let c = new Cat('Miao') // Miao
console.log(c.sayHi()) // Cat, My name is Miao

// setter 和 getter 
class Animal {
  constructor(name) {
    this.name = name
  }
  get name() {
    reutrn 'Jack'
  }
  set name(value) {
    console.log('setter: ' + value)
  }
}
let a = new Animal('kitty'); // setter: Kitty
a.name = 'TT' // setter: TT
console.log(a.name) // Jack

// 静态方法:可以通过类名直接调用的方法
class Animal {
  static isAnimal(a) {
    return a instanceof Animal
  }
}
let a = new Animal('jack')
Animal.isAnimal(a) //true
a.isAnimal(a) // Error: a.isAnimal is not a function

// 实例属性
class Animal {
  name = 'Jack' // 实例可以直接调用
  constructor() {

  }
}
let a = new Animal()
console.log(a.name) // Jack

// 静态属性,类可以直接调用的属性
class Animal {
  static num = 42
  constructor() {

  }
}
console.log(Animal.num) // 42

# TypeScript中类的用法

修饰符、抽象类等

# 访问修饰符public、private、protected

TypeScript 可以使用三种访问修饰符,分别是:public,private, protected

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部使用,包括子类
  • protected 修饰符是受保护的,和private类似,区别是它在子类中是允许被访问的。

# private、protected属性示例

// 示例1
class Animal {
  public name;
  public constructor(name) {
    this.name = name
  }
}
let a = new Animal('Jack')
console.log(a.name) // Jack
a.name = 'Tom'
console.log(a.name) // Tom

// 示例2
class Animal {
  private name;
  // protected name;
  public constructor(name) {
    this.name = name
  }
  get getName() {
    return this.name
  }
}
class Cat extends Animal {
  constructor(name) {
    super(name)
    console.log('cat', name)
  }
  get catName() {
    return this.name
  }
}
let a = new Animal('Jack')
console.log(a.getName) // Jack
console.log(a.name) // Error
a.name = 'Tom' // Error
// Property 'name' is private and only accessible within class 'Animal'.

let ce = new Cat('xx')
console.log(ce.name) // Error 不管是protected还是private
console.log(ce.catName) // protected name 则ok,private name 则Error

# private、protected用来修饰构造函数

当构造函数修饰为 private 时,该类不允许被继承或者实例化

class Animal {
    public name;
    private constructor (name) {
        this.name = name;
  }
}
class Cat extends Animal { // Error 继承
    constructor (name) {
        super(name);
    }
}

let a = new Animal('Jack'); // Error 实例化

当构造函数修饰为 protected 时,该类只允许被继承

class Animal {
    public name;
    protected constructor (name) {
        this.name = name;
  }
}
class Cat extends Animal { // OK
    constructor (name) {
        super(name);
    }
}

let a = new Animal('Jack'); // Error

# 在构造函数参数中使用修饰符

等同于类中定义该属性,使代码更简洁

class Animal {
    // public name: string;
    public constructor (public name) {
        this.name = name;
    }
}

# 只读属性关键字readonly

class Animal {
    readonly name;
    public constructor(name) {
        this.name = name;
    }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom'; // Error

如果readonly和其他访问修饰符一起使用,需要写在其后面。

class Animal {
    // public readonly name;
    public constructor(public readonly name) {
        this.name = name;
    }
}

# 抽象类(abstract)

abstract 用于定义抽象类和其中的抽象方法

# 抽象类是不允许被实例化的

abstract class Animal { // 抽象类
    public name; 
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi(); // 抽象方法
}

let a = new Animal('Jack'); // Error 不允许被实例化

# 抽象类中的抽象方法必须被子类实现

abstract class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi();
}

class Cat extends Animal { // Error,子类没有实现抽象方法
    public eat() {
        console.log(`${this.name} is eating.`);
    }
}

let cat = new Cat('Tom');

# 正确示例

注意 TypeScript的编译结果中,仍然存在这个抽象类

abstract class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi();
}

class Cat extends Animal {
    public sayHi() {
        console.log(`Meow, My name is ${this.name}`);
    }
}

let cat = new Cat('Tom');

# 类类型

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name
  }
  sayHi(): string {
    return `My name is ${this.name}`
  }
}

let a: Animal = new Animal('Jack');
console.log(a.sayHi()) // My name is Jack
上次更新: 2022/12/19 02:18:42