Skip to content

类的访问修饰符

TypeScript 提供了多种访问修饰符来控制类成员的可见性和可修改性。合理使用这些修饰符可以实现良好的封装,提高代码的安全性和可维护性。

public 修饰符#

public 是默认的访问修饰符,成员可以在任何地方访问:

// TypeScript 5.x
class User {
public name: string // 显式声明 public
age: number // 默认就是 public
public constructor(name: string, age: number) {
this.name = name
this.age = age
}
public greet(): string {
return `Hello, ${this.name}`
}
}
const user = new User('张三', 25)
console.log(user.name) // ✅ 可以访问
console.log(user.age) // ✅ 可以访问
user.name = '李四' // ✅ 可以修改

private 修饰符#

private 成员只能在类内部访问:

class BankAccount {
private balance: number
constructor(initialBalance: number) {
this.balance = initialBalance
}
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount
this.logTransaction('deposit', amount)
}
}
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
}
private logTransaction(type: string, amount: number): void {
console.log(`${type}: ${amount}, Balance: ${this.balance}`)
}
}
const account = new BankAccount(1000)
account.deposit(500)
// account.balance; // ❌ 错误:私有属性
// account.logTransaction('test', 100); // ❌ 错误:私有方法
console.log(account.getBalance()) // ✅ 1500

TypeScript private vs ES2022 #private#

// TypeScript private(编译时检查)
class A {
private x: number = 1
}
// ES2022 原生私有字段(运行时强制)
class B {
#x: number = 1
getX() {
return this.#x
}
}
// 区别:
// 1. TypeScript private 编译后变成普通属性,可被绕过
// 2. # 语法是真正的私有,运行时也无法访问
// 3. # 语法不能与 private 修饰符一起使用

protected 修饰符#

protected 成员可以在类内部和子类中访问:

class Animal {
protected name: string
protected age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
protected makeSound(): void {
console.log('Some sound')
}
}
class Dog extends Animal {
private breed: string
constructor(name: string, age: number, breed: string) {
super(name, age)
this.breed = breed
}
public introduce(): string {
// ✅ 可以访问 protected 成员
return `我是 ${this.name}${this.age} 岁,品种是 ${this.breed}`
}
public bark(): void {
this.makeSound() // ✅ 可以调用 protected 方法
console.log('汪汪!')
}
}
const dog = new Dog('旺财', 3, '柴犬')
console.log(dog.introduce())
// dog.name; // ❌ 错误:protected 成员不能在外部访问
// dog.makeSound(); // ❌ 错误

protected constructor#

// 受保护的构造函数,不能直接实例化
class Base {
protected constructor(public id: string) {}
}
// const base = new Base('1'); // ❌ 错误:构造函数受保护
class Derived extends Base {
constructor(
id: string,
public name: string
) {
super(id) // ✅ 子类可以调用
}
static create(id: string, name: string): Derived {
return new Derived(id, name)
}
}
const derived = new Derived('1', '测试')
// 或使用工厂方法
const derived2 = Derived.create('2', '测试2')

readonly 修饰符#

readonly 属性只能在声明时或构造函数中赋值:

class Config {
readonly apiUrl: string
readonly timeout: number
readonly version = '1.0.0' // 声明时赋值
constructor(apiUrl: string, timeout: number) {
this.apiUrl = apiUrl // 构造函数中赋值
this.timeout = timeout
}
// ❌ 不能在其他方法中赋值
// updateUrl(url: string) {
// this.apiUrl = url; // 错误
// }
}
const config = new Config('https://api.example.com', 5000)
// config.apiUrl = 'xxx'; // ❌ 错误:只读属性

readonly 与 const 的区别#

class Example {
readonly prop = 1 // 只读属性
method() {
const local = 2 // 常量变量
// local = 3; // ❌ 错误
// this.prop = 3; // ❌ 错误
}
}
// readonly 用于属性,const 用于变量
// 两者都表示不可重新赋值

static 修饰符#

static 成员属于类本身,而不是实例:

class Counter {
static count: number = 0
static readonly MAX_COUNT = 100
id: number
constructor() {
Counter.count++
this.id = Counter.count
}
static getCount(): number {
return Counter.count
}
static reset(): void {
Counter.count = 0
}
}
const c1 = new Counter()
const c2 = new Counter()
const c3 = new Counter()
console.log(Counter.count) // 3
console.log(Counter.getCount()) // 3
console.log(c1.id, c2.id, c3.id) // 1, 2, 3
Counter.reset()
console.log(Counter.count) // 0

静态块(ES2022)#

class Database {
static connection: Connection
static isInitialized = false
// 静态初始化块
static {
try {
Database.connection = createConnection()
Database.isInitialized = true
console.log('Database initialized')
} catch (error) {
console.error('Failed to initialize database')
}
}
}

修饰符组合#

修饰符可以组合使用:

class Example {
public readonly id: string
private readonly secret: string
protected static count: number = 0
constructor(id: string, secret: string) {
this.id = id
this.secret = secret
Example.count++
}
// 静态私有方法
private static log(message: string): void {
console.log(`[Example] ${message}`)
}
// 受保护的静态属性
protected static getCount(): number {
return Example.count
}
}

参数属性与修饰符#

class User {
constructor(
public readonly id: string,
public name: string,
private password: string,
protected role: string = 'user'
) {}
checkPassword(input: string): boolean {
return this.password === input
}
}
const user = new User('1', '张三', 'secret123')
console.log(user.id) // ✅
console.log(user.name) // ✅
// user.id = '2'; // ❌ readonly
// user.password; // ❌ private

访问性对比#

修饰符类内部子类实例
public
protected
private

实际应用模式#

单例模式#

class Singleton {
private static instance: Singleton
private constructor(public value: string) {}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton('singleton value')
}
return Singleton.instance
}
}
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2) // true

工厂模式#

class Product {
private constructor(
public readonly id: string,
public readonly name: string,
public readonly price: number
) {}
static create(name: string, price: number): Product {
const id = Math.random().toString(36).substr(2, 9)
return new Product(id, name, price)
}
static createFromJson(json: string): Product {
const data = JSON.parse(json)
return new Product(data.id, data.name, data.price)
}
}
const product = Product.create('手机', 5999)
console.log(product.id, product.name, product.price)

封装状态#

class StateMachine {
private currentState: string
private readonly transitions: Map<string, string[]>
constructor(initialState: string) {
this.currentState = initialState
this.transitions = new Map()
}
addTransition(from: string, to: string): this {
const existing = this.transitions.get(from) || []
existing.push(to)
this.transitions.set(from, existing)
return this
}
transition(to: string): boolean {
const allowed = this.transitions.get(this.currentState) || []
if (allowed.includes(to)) {
this.currentState = to
return true
}
return false
}
get state(): string {
return this.currentState
}
}
const machine = new StateMachine('idle')
.addTransition('idle', 'running')
.addTransition('running', 'paused')
.addTransition('running', 'stopped')
.addTransition('paused', 'running')
machine.transition('running') // true
console.log(machine.state) // "running"

常见问题#

🙋 private 成员会被继承吗?#

会被继承,但不能访问:

class Parent {
private secret = 'secret'
}
class Child extends Parent {
reveal() {
// return this.secret; // ❌ 错误:不能访问
return 'Cannot access parent private'
}
}

🙋 静态成员可以访问实例成员吗?#

不能直接访问:

class Example {
instanceProp = 'instance'
static staticMethod() {
// console.log(this.instanceProp); // ❌ 错误
}
static staticMethodWithInstance(instance: Example) {
console.log(instance.instanceProp) // ✅ 通过参数访问
}
}

🙋 如何让属性只在类内部可写?#

结合 private 和 public getter:

class Counter {
private _count = 0
get count(): number {
return this._count
}
increment(): void {
this._count++
}
}
const counter = new Counter()
console.log(counter.count) // ✅ 可读
// counter.count = 10; // ❌ 没有 setter,不可写
counter.increment() // ✅ 通过方法修改

总结#

修饰符作用适用场景
public公开访问公共 API
private仅类内部内部实现细节
protected类和子类可继承的内部成员
readonly只读不变的配置、常量
static类级别工具方法、单例、常量

下一篇我们将学习类的继承,包括 extends、super 和方法重写。