Skip to content

访问修饰符

概述

访问修饰符(Access Modifiers)是 TypeScript 中控制类成员可见性的重要机制。通过访问修饰符,我们可以精确控制哪些成员可以在类外部访问,哪些只能在类内部访问,哪些可以在子类中访问。TypeScript 提供了三种访问修饰符:public(公共)、private(私有)和 protected(受保护)。合理使用访问修饰符可以提高代码的封装性、安全性和可维护性,是面向对象编程中封装原则的重要体现。

基本语法

public(公共)

public 是默认的访问修饰符,成员可以在任何地方访问。即使不显式声明 public,类成员默认也是公共的:

typescript
class Person {
  // 显式声明 public(可选)
  public name: string;
  public age: number;
  
  // 不声明修饰符,默认也是 public
  email: string;
  
  constructor(name: string, age: number, email: string) {
    this.name = name;
    this.age = age;
    this.email = email;
  }
  
  // 公共方法
  public introduce(): void {
    console.log(`Hi, I'm ${this.name}, ${this.age} years old.`);
  }
  
  // 默认也是公共方法
  getEmail(): string {
    return this.email;
  }
}

// 在任何地方都可以访问 public 成员
const person = new Person("Alice", 30, "alice@example.com");
console.log(person.name);      // ✅ 可以访问
console.log(person.age);       // ✅ 可以访问
console.log(person.email);     // ✅ 可以访问
person.introduce();            // ✅ 可以调用
console.log(person.getEmail()); // ✅ 可以调用

private(私有)

private 成员只能在类内部访问,类外部和子类都不能访问:

typescript
class BankAccount {
  private balance: number;  // 私有属性
  public accountNumber: string;
  
  constructor(accountNumber: string, initialBalance: number) {
    this.accountNumber = accountNumber;
    this.balance = initialBalance;
  }
  
  // 公共方法访问私有属性
  public deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount;
      console.log(`Deposited ${amount}. New balance: ${this.balance}`);
    }
  }
  
  public withdraw(amount: number): boolean {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount;
      console.log(`Withdrew ${amount}. Remaining balance: ${this.balance}`);
      return true;
    }
    console.log("Insufficient funds");
    return false;
  }
  
  public getBalance(): number {
    return this.balance;  // 类内部可以访问 private 成员
  }
  
  // 私有方法
  private validateAmount(amount: number): boolean {
    return amount > 0;
  }
  
  public safeWithdraw(amount: number): boolean {
    if (this.validateAmount(amount)) {  // 类内部可以调用私有方法
      return this.withdraw(amount);
    }
    return false;
  }
}

const account = new BankAccount("ACC001", 1000);
account.deposit(500);           // ✅ 可以调用公共方法
console.log(account.getBalance()); // ✅ 可以调用公共方法,返回 1500

// ❌ 错误:不能直接访问私有属性
// console.log(account.balance); // Error: Property 'balance' is private and only accessible within class 'BankAccount'

// ❌ 错误:不能调用私有方法
// account.validateAmount(100); // Error: Property 'validateAmount' is private and only accessible within class 'BankAccount'

protected(受保护)

protected 成员可以在类内部和子类中访问,但不能在类外部访问:

typescript
class Animal {
  protected name: string;  // 受保护属性
  private id: number;      // 私有属性
  
  constructor(name: string, id: number) {
    this.name = name;
    this.id = id;
  }
  
  // 受保护方法
  protected makeSound(): void {
    console.log(`${this.name} makes a sound`);
  }
  
  // 公共方法
  public introduce(): void {
    console.log(`I am ${this.name}`);
  }
  
  // 公共方法访问受保护成员
  public getName(): string {
    return this.name;  // 类内部可以访问 protected 成员
  }
}

class Dog extends Animal {
  private breed: string;
  
  constructor(name: string, id: number, breed: string) {
    super(name, id);
    this.breed = breed;
  }
  
  // 子类可以访问父类的 protected 成员
  public bark(): void {
    this.makeSound();  // ✅ 子类可以调用父类的 protected 方法
    console.log(`${this.name} barks: Woof!`);  // ✅ 子类可以访问父类的 protected 属性
  }
  
  public getInfo(): string {
    return `${this.name} is a ${this.breed}`;  // ✅ 可以访问 protected 属性
  }
  
  // ❌ 错误:子类不能访问父类的 private 成员
  // public getId(): number {
  //   return this.id;  // Error: Property 'id' is private and only accessible within class 'Animal'
  // }
}

const dog = new Dog("Buddy", 1, "Golden Retriever");
dog.bark();              // ✅ 可以调用公共方法
dog.introduce();         // ✅ 可以调用继承的公共方法
console.log(dog.getInfo()); // ✅ "Buddy is a Golden Retriever"

// ❌ 错误:外部不能访问 protected 成员
// dog.makeSound();       // Error: Property 'makeSound' is protected and only accessible within class 'Animal' and its subclasses
// console.log(dog.name); // Error: Property 'name' is protected and only accessible within class 'Animal' and its subclasses

访问修饰符对比

可见性总结

访问修饰符类内部子类类外部说明
public任何地方都可以访问(默认)
protected只能在类内部和子类中访问
private只能在类内部访问

使用示例对比

typescript
class Example {
  public publicProp: string = "public";
  protected protectedProp: string = "protected";
  private privateProp: string = "private";
  
  public publicMethod(): void {
    console.log("Public method");
    // 类内部可以访问所有成员
    console.log(this.publicProp);      // ✅
    console.log(this.protectedProp);    // ✅
    console.log(this.privateProp);     // ✅
  }
  
  protected protectedMethod(): void {
    console.log("Protected method");
  }
  
  private privateMethod(): void {
    console.log("Private method");
  }
}

class Derived extends Example {
  public testAccess(): void {
    // 子类可以访问 public 和 protected
    console.log(this.publicProp);      // ✅
    console.log(this.protectedProp);   // ✅
    // console.log(this.privateProp);  // ❌ 错误:不能访问 private
    
    this.publicMethod();               // ✅
    this.protectedMethod();            // ✅
    // this.privateMethod();           // ❌ 错误:不能调用 private 方法
  }
}

const example = new Example();
const derived = new Derived();

// 外部访问
console.log(example.publicProp);      // ✅
// console.log(example.protectedProp); // ❌ 错误
// console.log(example.privateProp);   // ❌ 错误

example.publicMethod();                // ✅
// example.protectedMethod();          // ❌ 错误
// example.privateMethod();            // ❌ 错误

构造函数参数中的访问修饰符

TypeScript 允许在构造函数参数中直接使用访问修饰符,这样可以自动创建并初始化属性:

typescript
class User {
  // 传统写法
  public name: string;
  private password: string;
  protected email: string;
  
  constructor(name: string, password: string, email: string) {
    this.name = name;
    this.password = password;
    this.email = email;
  }
}

// 简化写法:在构造函数参数中使用访问修饰符
class UserSimplified {
  constructor(
    public name: string,        // 自动创建公共属性
    private password: string,   // 自动创建私有属性
    protected email: string     // 自动创建受保护属性
  ) {
    // 不需要手动赋值,TypeScript 会自动处理
  }
  
  public verifyPassword(input: string): boolean {
    return this.password === input;  // 可以访问私有属性
  }
  
  public getEmail(): string {
    return this.email;  // 可以访问受保护属性
  }
}

const user = new UserSimplified("Alice", "secret123", "alice@example.com");
console.log(user.name);              // ✅ "Alice"
// console.log(user.password);        // ❌ 错误:私有属性
// console.log(user.email);           // ❌ 错误:受保护属性
console.log(user.verifyPassword("secret123")); // ✅ true
console.log(user.getEmail());        // ✅ "alice@example.com"

使用示例

示例 1:用户认证系统

typescript
class User {
  public username: string;
  private password: string;      // 私有:不应该直接暴露
  protected email: string;       // 受保护:子类可能需要访问
  public isActive: boolean;
  
  constructor(username: string, password: string, email: string) {
    this.username = username;
    this.password = password;  // 实际应用中应该加密
    this.email = email;
    this.isActive = true;
  }
  
  // 公共方法:验证密码
  public verifyPassword(inputPassword: string): boolean {
    return this.password === inputPassword;  // 类内部可以访问私有属性
  }
  
  // 公共方法:更改密码
  public changePassword(oldPassword: string, newPassword: string): boolean {
    if (this.verifyPassword(oldPassword)) {
      this.password = newPassword;  // 类内部可以修改私有属性
      return true;
    }
    return false;
  }
  
  // 受保护方法:子类可能需要重写
  protected sendEmail(subject: string, body: string): void {
    console.log(`Sending email to ${this.email}: ${subject} - ${body}`);
  }
  
  // 公共方法:激活账户
  public activate(): void {
    this.isActive = true;
    this.sendEmail("Account Activated", "Your account has been activated.");
  }
  
  public deactivate(): void {
    this.isActive = false;
    this.sendEmail("Account Deactivated", "Your account has been deactivated.");
  }
}

class AdminUser extends User {
  private permissions: string[];
  
  constructor(username: string, password: string, email: string, permissions: string[]) {
    super(username, password, email);
    this.permissions = permissions;
  }
  
  // 子类可以访问父类的 protected 成员
  public sendAdminNotification(message: string): void {
    this.sendEmail("Admin Notification", message);  // ✅ 可以调用 protected 方法
    console.log(`Admin ${this.username} (${this.email}) received notification`);  // ✅ 可以访问 protected 属性
  }
  
  public hasPermission(permission: string): boolean {
    return this.permissions.includes(permission);
  }
  
  // ❌ 错误:子类不能访问父类的 private 成员
  // public getPassword(): string {
  //   return this.password;  // Error: Property 'password' is private
  // }
}

const user = new User("alice", "password123", "alice@example.com");
const admin = new AdminUser("admin", "admin123", "admin@example.com", ["read", "write", "delete"]);

// 公共成员可以访问
console.log(user.username);  // ✅ "alice"
user.activate();             // ✅ 可以调用

// 私有成员不能直接访问
// console.log(user.password);  // ❌ 错误

// 受保护成员不能直接访问
// console.log(user.email);     // ❌ 错误
// user.sendEmail("Test", "Test");  // ❌ 错误

// 子类可以访问受保护成员(通过公共方法)
admin.sendAdminNotification("System maintenance scheduled");

示例 2:银行账户系统

typescript
class BankAccount {
  private accountNumber: string;  // 私有:账户号不应该直接暴露
  private balance: number;         // 私有:余额应该通过方法访问
  protected owner: string;         // 受保护:子类可能需要访问
  public accountType: string;      // 公共:账户类型可以公开
  
  constructor(accountNumber: string, owner: string, accountType: string, initialBalance: number = 0) {
    this.accountNumber = accountNumber;
    this.owner = owner;
    this.accountType = accountType;
    this.balance = initialBalance;
  }
  
  // 公共方法:存款
  public deposit(amount: number): boolean {
    if (amount > 0) {
      this.balance += amount;
      this.logTransaction("deposit", amount);
      return true;
    }
    return false;
  }
  
  // 公共方法:取款
  public withdraw(amount: number): boolean {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount;
      this.logTransaction("withdraw", amount);
      return true;
    }
    return false;
  }
  
  // 公共方法:查询余额
  public getBalance(): number {
    return this.balance;
  }
  
  // 公共方法:获取账户信息
  public getAccountInfo(): string {
    return `Account ${this.maskAccountNumber()} - Owner: ${this.owner} - Type: ${this.accountType}`;
  }
  
  // 私有方法:记录交易
  private logTransaction(type: string, amount: number): void {
    console.log(`[${new Date().toISOString()}] ${type.toUpperCase()}: $${amount} - Balance: $${this.balance}`);
  }
  
  // 私有方法:掩码账户号
  private maskAccountNumber(): string {
    return `****${this.accountNumber.slice(-4)}`;
  }
  
  // 受保护方法:子类可以重写
  protected calculateInterest(): number {
    return 0;  // 基础账户没有利息
  }
}

class SavingsAccount extends BankAccount {
  private interestRate: number;
  
  constructor(accountNumber: string, owner: string, initialBalance: number, interestRate: number) {
    super(accountNumber, owner, "Savings", initialBalance);
    this.interestRate = interestRate;
  }
  
  // 重写受保护方法
  protected calculateInterest(): number {
    return this.getBalance() * (this.interestRate / 100);  // 可以访问受保护方法
  }
  
  // 公共方法:计算并添加利息
  public applyInterest(): void {
    const interest = this.calculateInterest();
    this.deposit(interest);  // 可以调用继承的公共方法
    console.log(`Interest of $${interest.toFixed(2)} applied to ${this.owner}'s account`);
  }
  
  public getOwner(): string {
    return this.owner;  // ✅ 子类可以访问 protected 属性
  }
  
  // ❌ 错误:不能访问父类的私有成员
  // public getAccountNumber(): string {
  //   return this.accountNumber;  // Error: Property 'accountNumber' is private
  // }
}

const savings = new SavingsAccount("1234567890", "Alice", 1000, 2.5);
savings.deposit(500);
console.log(savings.getBalance());        // 1500
console.log(savings.getAccountInfo());    // "Account ****7890 - Owner: Alice - Type: Savings"
savings.applyInterest();                  // 计算并应用利息
console.log(savings.getOwner());          // "Alice"

// ❌ 错误:不能直接访问私有或受保护成员
// console.log(savings.accountNumber);  // Error
// console.log(savings.balance);       // Error
// console.log(savings.owner);         // Error: protected

示例 3:车辆管理系统

typescript
class Vehicle {
  protected brand: string;        // 受保护:子类需要访问
  protected model: string;         // 受保护:子类需要访问
  private vin: string;             // 私有:车辆识别号不应该直接暴露
  public year: number;             // 公共:年份可以公开
  
  constructor(brand: string, model: string, vin: string, year: number) {
    this.brand = brand;
    this.model = model;
    this.vin = vin;
    this.year = year;
  }
  
  // 公共方法:获取车辆信息
  public getInfo(): string {
    return `${this.year} ${this.brand} ${this.model}`;
  }
  
  // 公共方法:获取 VIN(不直接暴露)
  public getVIN(): string {
    return this.maskVIN();  // 通过方法访问私有属性
  }
  
  // 受保护方法:启动(子类可以重写)
  protected start(): void {
    console.log(`${this.brand} ${this.model} is starting...`);
  }
  
  // 受保护方法:停止(子类可以重写)
  protected stop(): void {
    console.log(`${this.brand} ${this.model} is stopping...`);
  }
  
  // 公共方法:驾驶
  public drive(): void {
    this.start();
    console.log(`${this.getInfo()} is driving`);
  }
  
  // 私有方法:掩码 VIN
  private maskVIN(): string {
    return `VIN: ****${this.vin.slice(-4)}`;
  }
}

class Car extends Vehicle {
  private doors: number;
  protected engineType: string;  // 受保护:子类可能需要访问
  
  constructor(brand: string, model: string, vin: string, year: number, doors: number, engineType: string) {
    super(brand, model, vin, year);
    this.doors = doors;
    this.engineType = engineType;
  }
  
  // 重写受保护方法
  protected start(): void {
    console.log(`Starting ${this.engineType} engine...`);  // 可以访问 protected 属性
    super.start();  // 调用父类方法
  }
  
  // 公共方法:获取详细信息
  public getDetails(): string {
    return `${this.getInfo()} - ${this.doors} doors, ${this.engineType} engine`;
    // 可以访问 protected 属性:brand, model, engineType
  }
}

class ElectricCar extends Car {
  private batteryCapacity: number;
  
  constructor(brand: string, model: string, vin: string, year: number, doors: number, batteryCapacity: number) {
    super(brand, model, vin, year, doors, "Electric");
    this.batteryCapacity = batteryCapacity;
  }
  
  // 重写受保护方法
  protected start(): void {
    console.log(`Charging battery...`);
    console.log(`Starting ${this.engineType} motor...`);  // 可以访问父类的 protected 属性
    super.start();
  }
  
  public getBatteryInfo(): string {
    return `${this.getDetails()} - Battery: ${this.batteryCapacity} kWh`;
  }
}

const car = new Car("Toyota", "Camry", "1HGBH41JXMN109186", 2023, 4, "V6");
const electricCar = new ElectricCar("Tesla", "Model 3", "5YJ3E1EA1KF123456", 2023, 4, 75);

console.log(car.getInfo());        // "2023 Toyota Camry"
console.log(car.getDetails());     // "2023 Toyota Camry - 4 doors, V6 engine"
console.log(car.getVIN());         // "VIN: ****9186"
car.drive();                        // 启动并驾驶

console.log(electricCar.getBatteryInfo()); // "2023 Tesla Model 3 - 4 doors, Electric engine - Battery: 75 kWh"
electricCar.drive();                // 充电、启动并驾驶

类型检查示例

常见错误

typescript
// ❌ 错误:外部访问私有成员
class Example {
  private secret: string = "secret";
}

const example = new Example();
console.log(example.secret);
// Error: Property 'secret' is private and only accessible within class 'Example'

// ❌ 错误:外部访问受保护成员
class Base {
  protected value: string = "value";
}

const base = new Base();
console.log(base.value);
// Error: Property 'value' is protected and only accessible within class 'Base' and its subclasses

// ❌ 错误:子类访问父类的私有成员
class Parent {
  private prop: string = "private";
}

class Child extends Parent {
  public getProp(): string {
    return this.prop;  // 错误:不能访问父类的私有成员
    // Error: Property 'prop' is private and only accessible within class 'Parent'
  }
}

// ❌ 错误:降低方法可见性
class Base {
  public method(): void {}
}

class Derived extends Base {
  private method(): void {}  // 错误:不能降低可见性
  // Error: Property 'method' in type 'Derived' is not assignable to the same property in base type 'Base'
}

正确写法

typescript
// ✅ 正确:通过公共方法访问私有成员
class Example {
  private secret: string = "secret";
  
  public getSecret(): string {
    return this.secret;  // 类内部可以访问
  }
}

const example = new Example();
console.log(example.getSecret());  // ✅ "secret"

// ✅ 正确:子类访问受保护成员
class Base {
  protected value: string = "value";
  
  public getValue(): string {
    return this.value;
  }
}

class Derived extends Base {
  public getProtectedValue(): string {
    return this.value;  // ✅ 子类可以访问 protected
  }
}

const derived = new Derived();
console.log(derived.getProtectedValue());  // ✅ "value"

// ✅ 正确:通过父类公共方法访问私有成员
class Parent {
  private prop: string = "private";
  
  public getProp(): string {
    return this.prop;
  }
}

class Child extends Parent {
  public accessProp(): string {
    return this.getProp();  // ✅ 通过公共方法访问
  }
}

// ✅ 正确:提高方法可见性
class Base {
  protected method(): void {}
}

class Derived extends Base {
  public method(): void {  // ✅ 可以将 protected 改为 public
    super.method();
  }
}

注意事项

提示

  • public 是默认的访问修饰符,可以省略不写
  • 使用 private 封装内部实现细节,只通过公共方法暴露接口
  • 使用 protected 允许子类访问,同时保持对外的封装性
  • 在构造函数参数中使用访问修饰符可以简化代码
  • 合理使用访问修饰符可以提高代码的可维护性和安全性

注意

  • 访问修饰符只在编译时生效,编译后的 JavaScript 代码中所有成员都是可访问的
  • TypeScript 的访问控制是编译时的类型检查,不会影响运行时的行为
  • private 成员在子类中不可访问,如果需要子类访问,使用 protected
  • 子类可以提高方法的可见性(如将 protected 改为 public),但不能降低
  • 在类外部无法访问 privateprotected 成员

重要

  • 访问修饰符是封装原则的重要体现,合理使用可以提高代码质量
  • 私有成员应该通过公共方法访问,而不是直接暴露
  • 受保护成员适合用于需要在子类中访问但不想对外暴露的场景
  • 过度使用 public 会破坏封装性,应该谨慎使用
  • 访问修饰符的选择应该基于实际需求和设计原则

信息

访问修饰符的选择原则

  1. public:需要对外暴露的接口、API

    • 类的公共接口
    • 需要外部访问的属性和方法
  2. protected:需要在子类中使用但不想对外暴露

    • 子类需要重写的方法
    • 子类需要访问的属性
    • 模板方法模式中的钩子方法
  3. private:内部实现细节

    • 不应该暴露的内部状态
    • 辅助方法
    • 敏感数据(如密码、密钥)

实际应用场景

  • 数据封装:使用 private 保护敏感数据
  • 继承设计:使用 protected 为子类提供扩展点
  • API 设计:使用 public 定义清晰的公共接口
  • 内部实现:使用 private 隐藏实现细节

相关链接

基于 VitePress 构建