TypeScript 类的核心概念详解

1. 抽象类(Abstract Class)

定义

抽象类是一种不能被直接实例化的特殊类,只能被其他类继承。它可以包含抽象方法(没有具体实现的方法)和具体方法。

用途

  1. 作为基类提供通用功能
  2. 定义接口规范
  3. 强制子类实现特定方法
  4. 共享公共代码

何时使用

  • 当多个类有共同的基础功能时
  • 需要强制子类实现某些方法时
  • 想要防止类被直接实例化时
abstract class Animal {
  // 抽象方法:必须在子类中实现
  abstract makeSound(): void;
  
  // 具体方法:提供通用实现
  move(): void {
    console.log('Moving...');
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log('Woof!');
  }
}

2. 静态成员(Static Members)

定义

静态成员属于类本身而不是类的实例,可以直接通过类名访问。

用途

  1. 存储类级别的数据
  2. 提供工具方法
  3. 实现单例模式
  4. 缓存数据

何时使用

  • 需要在所有实例间共享数据时
  • 不需要访问实例数据的工具方法
  • 需要计数器或缓存时
class Database {
  // 静态属性:所有实例共享
  private static instance: Database;
  private static connectionCount: number = 0;
  
  // 静态方法:不需要实例化就能调用
  static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }
  
  // 静态方法可以访问静态属性
  static getConnectionCount(): number {
    return Database.connectionCount;
  }
}

3. 访问修饰符(Access Modifiers)

定义

访问修饰符控制类成员的可访问性和可见性。

种类和作用域

  1. public(默认)
  • 定义:可以在任何地方访问
  • 作用域:类内部、类实例、子类、外部代码
  • 用途:定义公共 API
  1. private
  • 定义:只能在声明的类内部访问
  • 作用域:仅类内部
  • 用途:隐藏内部实现细节
  1. protected
  • 定义:可以在声明的类及其子类中访问
  • 作用域:类内部和子类
  • 用途:允许子类访问基类功能

何时使用各种修饰符

  1. 使用 public 当:
  • 需要对外暴露 API
  • 属性/方法需要在类外部访问
  • 是公共接口的一部分
  1. 使用 private 当:
  • 需要隐藏实现细节
  • 防止外部直接修改状态
  • 需要严格控制访问
  1. 使用 protected 当:
  • 需要在继承链中共享功能
  • 子类需要访问父类的某些功能
  • 想要限制外部访问但允许子类访问
class BankAccount {
  // public:对外可见的 API
  public accountNumber: string;
  
  // private:内部实现细节
  private balance: number;
  
  // protected:允许子类访问
  protected transactionHistory: Transaction[];
  
  constructor(accountNumber: string) {
    this.accountNumber = accountNumber;
    this.balance = 0;
    this.transactionHistory = [];
  }
  
  // public 方法:对外 API
  public deposit(amount: number): void {
    this.updateBalance(amount);
  }
  
  // private 方法:内部实现
  private updateBalance(amount: number): void {
    this.balance += amount;
    this.logTransaction(amount);
  }
  
  // protected 方法:子类可用
  protected logTransaction(amount: number): void {
    this.transactionHistory.push({
      amount,
      date: new Date()
    });
  }
}

4. 属性修饰符(Property Modifiers)

readonly

  • 定义:只能在声明时或构造函数中赋值
  • 用途:防止属性被修改
  • 何时使用:常量、配置值、不可变数据
class Config {
  readonly API_KEY: string;
  readonly MAX_RETRIES: number = 3;
  
  constructor(apiKey: string) {
    this.API_KEY = apiKey;  // 只能在这里赋值
  }
}

可选属性(?)

  • 定义:属性可以不存在
  • 用途:表示可选的类成员
  • 何时使用:处理可选配置、部分初始化
class UserProfile {
  name: string;
  age?: number;  // 可选属性
  bio?: string;  // 可选属性
}

5. 方法修饰符

abstract

  • 定义:在抽象类中声明但不实现的方法
  • 用途:强制子类提供实现
  • 何时使用:需要确保子类实现特定行为时
abstract class Shape {
  abstract calculateArea(): number;  // 子类必须实现
  abstract calculatePerimeter(): number;  // 子类必须实现
  
  // 具体方法可以使用抽象方法
  printDetails(): void {
    console.log(`Area: ${this.calculateArea()}`);
    console.log(`Perimeter: ${this.calculatePerimeter()}`);
  }
}

override

  • 定义:明确指示方法重写父类方法
  • 用途:增加代码可读性,防止意外重写
  • 何时使用:有意重写父类方法时
class Parent {
  greet(): void {
    console.log("Hello");
  }
}

class Child extends Parent {
  override greet(): void {  // 明确表示重写
    console.log("Hi there");
  }
}

最佳实践总��

  1. 封装原则
  • 默认使用 private
  • 只有需要时才改为 protected 或 public
  • 使用访问器方法控制属性访问
  1. 继承设计
  • 优先使用组合而不是继承
  • 抽象类用于定义契约
  • 使用 protected 而不是 public 共享功能
  1. 静态成员使用
  • 用于工具方法和共享状态
  • 避免过度使用静态成员
  • 考虑使用单例模式替代静态类
  1. 修饰符选择
  • 明确每个成员的可见性需求
  • 使用 readonly 保护不可变数据
  • 合理使用可选属性处理可选状态