访问修饰符

访问修饰符控制类成员的可访问性,是封装的关键机制。TypeScript 提供了三种访问修饰符:publicprivateprotected,分别对应不同的访问级别。

public 公共成员

public 修饰的成员可以在任何地方访问,这是默认的访问级别。

class User {
  public name: string
  public age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }

  public greet(): string {
    return `你好,我是 ${this.name}`
  }
}

const user = new User("张三", 25)
console.log(user.name)
console.log(user.greet())

nameagegreet 方法都是公共成员,可以在类外部自由访问。省略修饰符时默认就是 public

private 私有成员

private 修饰的成员只能在类内部访问,外部无法直接访问。

class BankAccount {
  private balance: number

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

  deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount
      console.log(`存入 ${amount},余额 ${this.balance}`)
    }
  }

  withdraw(amount: number): boolean {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount
      console.log(`取出 ${amount},余额 ${this.balance}`)
      return true
    }
    console.log("取款失败")
    return false
  }

  getBalance(): number {
    return this.balance
  }
}

const account = new BankAccount(1000)
account.deposit(500)
account.withdraw(200)
console.log(account.getBalance())

balance 是私有属性,外部无法直接修改,只能通过 depositwithdraw 方法操作。这保证了数据的安全性。

protected 受保护成员

protected 修饰的成员可以在类内部和子类中访问,但不能在类外部访问。

class Component {
  protected state: any
  protected props: any

  constructor(props: any) {
    this.props = props
    this.state = {}
  }

  protected setState(newState: any): void {
    this.state = { ...this.state, ...newState }
  }
}

class Button extends Component {
  constructor(props: any) {
    super(props)
  }

  click(): void {
    this.setState({ clicked: true })
    console.log("按钮被点击", this.state)
  }
}

const button = new Button({ text: "提交" })
button.click()

stateprops 是受保护成员,子类 Button 可以访问它们,但外部代码无法直接访问。

参数属性简写

在构造函数参数上添加访问修饰符,可以同时声明和初始化属性。

class Product {
  constructor(
    public name: string,
    public price: number,
    private stock: number,
    protected category: string
  ) {}

  getStock(): number {
    return this.stock
  }
}

const product = new Product("手机", 2999, 100, "电子产品")
console.log(product.name)
console.log(product.price)
console.log(product.getStock())

这种简写方式减少了样板代码,让类定义更加简洁。nameprice 是公共属性,stock 是私有属性,category 是受保护属性。

访问级别对比

三种访问修饰符的可访问范围如下:

修饰符类内部子类类外部
public
protected
private

选择合适的访问级别是良好封装的关键。通常将数据设为私有,通过公共方法提供受控的访问。

私有字段的 ES 实现

TypeScript 还支持 JavaScript 的私有字段语法,使用 # 前缀。

class Counter {
  #count: number = 0

  increment(): void {
    this.#count++
  }

  decrement(): void {
    this.#count--
  }

  getCount(): number {
    return this.#count
  }
}

const counter = new Counter()
counter.increment()
counter.increment()
console.log(counter.getCount())

#count 是真正的私有字段,即使在运行时也无法从外部访问。这与 private 不同,private 只在编译时检查,运行时仍然可以访问。

访问修饰符与存取器

访问修饰符可以与存取器配合使用,实现更精细的访问控制。

class User {
  private _password: string = ""

  get password(): string {
    return "******"
  }

  set password(value: string) {
    if (value.length >= 6) {
      this._password = value
    } else {
      console.log("密码长度至少6位")
    }
  }

  validatePassword(input: string): boolean {
    return this._password === input
  }
}

const user = new User()
user.password = "123456"
console.log(user.password)
console.log(user.validatePassword("123456"))

_password 是私有属性,外部无法直接读取。通过存取器,我们控制了密码的设置和显示方式。

实际应用场景

访问修饰符在实际开发中有很多应用场景。比如实现单例模式:

class Database {
  private static instance: Database
  private connection: any

  private constructor() {
    this.connection = {}
  }

  static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database()
    }
    return Database.instance
  }

  query(sql: string): any {
    console.log(`执行查询: ${sql}`)
    return []
  }
}

const db1 = Database.getInstance()
const db2 = Database.getInstance()
console.log(db1 === db2)

构造函数设为私有,外部无法直接创建实例,只能通过 getInstance 方法获取唯一的实例。

小结

访问修饰符是封装的核心工具。public 允许任意访问,private 限制只能在类内部访问,protected 允许子类访问。合理使用访问修饰符可以保护数据安全,控制类的接口,提高代码的可维护性。在实际开发中,应该默认使用最小的访问级别,只在必要时开放访问权限。