Skip to content

在 JavaScript ES6 之前,开发者们通过原型链(prototypal inheritance)来模拟面向对象的编程范式。虽然功能强大,但其语法和心智模型对于有传统面向对象语言(如 Java 或 C#)背景的开发者来说,常常显得不够直观。ES6 引入了 class 关键字,提供了一套更清晰、更熟悉的语法糖。而 TypeScript 则在此基础上,更进一步,引入了完整的类型支持和强大的访问控制机制,将 JavaScript 的面向对象能力提升到了一个全新的工程化高度。

本文将深入探讨 TypeScript 中类的定义,并重点解析其访问控制的核心——publicprivateprotected 修饰符。

类的基本解剖:蓝图的构建

在面向对象编程中,类 (Class) 是创建对象的蓝图 (Blueprint)。它定义了一类事物所共有的属性 (Properties)(状态)和方法 (Methods)(行为)。

让我们通过构建一个简单的 Player 类来理解其基本结构:

typescript
class Player {
  // 1. 属性 (Properties)
  username: string;
  health: number;
  isOnline: boolean;

  // 2. 构造函数 (Constructor)
  constructor(username: string) {
    this.username = username;
    this.health = 100; // 初始生命值为 100
    this.isOnline = true;
    console.log(`Welcome, ${this.username}!`);
  }

  // 3. 方法 (Methods)
  attack(target: Player): void {
    if (!this.isOnline || !target.isOnline) {
      console.log("One of the players is offline.");
      return;
    }
    console.log(`${this.username} attacks ${target.username}!`);
    // ... 复杂的伤害计算逻辑
  }

  disconnect(): void {
    this.isOnline = false;
    console.log(`${this.username} has disconnected.`);
  }
}

这个 Player 类包含了构成一个类的三个核心部分:

  1. 属性:我们在类的主体中声明了 username, health, isOnline 等属性,并为它们指定了类型。这些属性构成了每个 Player 实例的内部状态。
  2. 构造函数constructor 是一个特殊的方法,它在通过 new 关键字创建类的新实例时被自动调用。它的主要职责是初始化对象的属性。
  3. 方法attackdisconnect 是定义在类上的函数,它们描述了 Player 对象能够执行的行为。方法可以访问和修改实例的属性(通过 this 关键字)。

创建和使用类的实例(即对象)非常直观:

typescript
const player1 = new Player("Alice");
const player2 = new Player("Bob");

player1.attack(player2); // 输出: Alice attacks Bob!
player2.disconnect();  // 输出: Bob has disconnected.

访问修饰符:封装的艺术

仅仅定义结构是不够的。一个健壮的系统还需要封装 (Encapsulation)——即隐藏对象的内部实现细节,只暴露必要的接口供外部使用。这可以防止外部代码随意篡改对象的状态,从而保证系统的稳定性和可维护性。

TypeScript 为此提供了三个访问修饰符:publicprivateprotected

1. public (公开的)

public 是最宽松的访问级别,也是默认的修饰符。如果你不显式指定任何修饰符,那么该成员就是 public 的。被标记为 public 的属性或方法可以在任何地方被访问——包括类的内部、类的实例外部,以及子类中。

typescript
class Greeter {
  public message: string; // 显式标记为 public

  constructor(message: string) {
    this.message = message;
  }
}

const greeter = new Greeter("Hello");
console.log(greeter.message); // 可以在外部访问

2. private (私有的)

private 是最严格的访问级别。被标记为 private 的成员只能在定义它的那个类的内部被访问。类的实例外部和子类都无法访问。这对于隐藏那些不应被外部知晓的内部状态或辅助方法至关重要。

typescript
class BankAccount {
  private balance: number = 0;

  constructor(initialBalance: number) {
    if (this.isValidAmount(initialBalance)) {
      this.balance = initialBalance;
    }
  }

  public deposit(amount: number): void {
    if (this.isValidAmount(amount)) {
      this.balance += amount;
      console.log(`Deposited ${amount}. New balance: ${this.balance}`);
    }
  }

  // 这是一个私有的辅助方法,外部无需关心其实现
  private isValidAmount(amount: number): boolean {
    return amount > 0;
  }
}

const account = new BankAccount(100);
account.deposit(50);

// console.log(account.balance); 
// 编译时错误: Property 'balance' is private and only accessible within class 'BankAccount'.

// account.isValidAmount(10);
// 编译时错误: Property 'isValidAmount' is private...

3. protected (受保护的)

protected 是介于 publicprivate 之间的一种访问级别。被标记为 protected 的成员可以在定义它的类的内部以及该类的子类中被访问。但是,它不能在类的实例外部被访问。

这对于创建可被子类扩展和定制的基类非常有用。

typescript
class Animal {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Dog extends Animal {
  public bark(): void {
    // 可以在子类中访问受保护的成员
    console.log(`Woof! My name is ${this.name}.`);
  }
}

const dog = new Dog("Buddy");
dog.bark(); // 输出: Woof! My name is Buddy.

// console.log(dog.name);
// 编译时错误: Property 'name' is protected and only accessible within class 'Animal' and its subclasses.

实践捷径:参数属性

TypeScript 提供了一种非常便利的语法糖,叫做参数属性 (Parameter Properties)。它允许我们在构造函数的参数上直接使用访问修饰符,从而将参数的声明、赋值和属性的定义合并为一步。

传统方式:

typescript
class Person {
  public name: string;
  public age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

使用参数属性的简洁方式:

typescript
class Person {
  constructor(public name: string, public age: number) {
    // 构造函数体可以是空的,因为声明和赋值已经自动完成
  }
}

这两种写法在功能上是完全等价的,但第二种显然更加简洁高效,是 TypeScript 开发中的常用技巧。

总结

类是 TypeScript 实现结构化和面向对象编程的核心。它通过将数据(属性)和行为(方法)封装在一起,为我们提供了构建复杂系统的坚实基础。

  • 是创建对象的蓝图,包含属性构造函数方法
  • 访问修饰符是实现封装的关键:
    • public:无限制访问(默认)。
    • private:仅限类内部访问。
    • protected:类内部及其子类可以访问。
  • 参数属性是简化类定义的实用语法糖。

通过熟练运用类及其访问控制机制,我们可以编写出更模块化、更安全、更易于维护和扩展的高质量代码。

不知道说啥了很无语了