TypeScript 访问修饰符作用域详解
访问修饰符概览
TypeScript 提供了三种访问修饰符:
- public(默认)
- private
- protected
详细说明
1. public 修饰符
作用域
- 类内部可以访问
- 类的实例可以访问
- 子类可以访问
- 类外部可以访问
示例
class Animal {
public name: string;
constructor(name: string) {
this.name = name; // 类内部访问
}
}
class Dog extends Animal {
bark() {
console.log(this.name); // 子类访问
}
}
const animal = new Animal("Pet");
console.log(animal.name); // 外部访问
2. private 修饰符
作用域
- 只能在声明的类内部访问
- 类的实例不能访问
- 子类不能访问
- 类外部不能访问
示例
class BankAccount {
private balance: number = 0;
constructor(initialBalance: number) {
this.balance = initialBalance; // 类内部访问 OK
}
private updateBalance(amount: number) {
this.balance += amount;
}
}
class ChildAccount extends BankAccount {
checkBalance() {
// this.balance; // 错误:子类不能访问
// this.updateBalance; // 错误:子类不能访问
}
}
const account = new BankAccount(100);
// account.balance; // 错误:实例不能访问
// account.updateBalance; // 错误:实例不能访问
3. protected 修饰符
作用域
- 类内部可以访问
- 子类可以访问
- 类的实例不能访问
- 类外部不能访问
示例
class Person {
protected age: number;
constructor(age: number) {
this.age = age; // 类内部访问 OK
}
protected getAge() {
return this.age;
}
}
class Employee extends Person {
getEmployeeAge() {
return this.age; // 子类访问 OK
this.getAge(); // 子类访问 OK
}
}
const person = new Person(30);
// person.age; // 错误:实例不能访问
// person.getAge(); // 错误:实例不能访问
访问修饰符对比表
| 访问位置 | public | private | protected |
|---|---|---|---|
| 类内部 | ✅ 可以 | ✅ 可以 | ✅ 可以 |
| 子类 | ✅ 可以 | ❌ 不可以 | ✅ 可以 |
| 实例 | ✅ 可以 | ❌ 不可以 | ❌ 不可以 |
| 类外部 | ✅ 可以 | ❌ 不可以 | ❌ 不可以 |
实际应用场景
1. 封装私有实现
class UserService {
private apiClient: ApiClient;
constructor() {
this.apiClient = new ApiClient(); // 私有实现细节
}
public async getUser(id: string) {
// 公共 API
return this.apiClient.get(`/users/${id}`);
}
}
2. 保护基类方法
abstract class Database {
protected abstract connect(): void;
protected abstract disconnect(): void;
public query(sql: string) {
this.connect();
// 执行查询
this.disconnect();
}
}
class MySQLDatabase extends Database {
protected connect() {
// MySQL 特定的连接逻辑
}
protected disconnect() {
// MySQL 特定的断开逻辑
}
}
3. 私有状态管理
class Counter {
private count: number = 0;
public increment() {
this.count++;
this.notifyUpdate();
}
public decrement() {
this.count--;
this.notifyUpdate();
}
private notifyUpdate() {
console.log(`Count updated: ${this.count}`);
}
public getCount() {
return this.count;
}
}
最佳实践
-
默认使用 private
- 除非确实需要在外部访问,否则优先使用 private
- 有助于减少代码耦合
-
谨慎使用 protected
- 只在确实需要子类访问时使用
- 避免过度暴露内部细节
-
使用 public 接口
- 为外部提供清晰的公共 API
- 隐藏实现细节
-
封装变化
- 使用私有成员封装可能变化的实现
- 通过公共方法提供稳定的接口
注意事项
-
JavaScript 运行时
- TypeScript 的访问修饰符在编译后会被移除
- 不能依赖访问修饰符来实现运行时的安全性
-
私有字段 vs private
- ES2019+ 提供了 # 私有字段语法
- 与 TypeScript 的 private 修饰符不同,# 私有字段在运行时也有效
-
构造函数参数
class Person {
constructor(
public name: string, // 自动创建公共属性
private age: number, // 自动创建私有属性
protected id: string // 自动创建受保护属性
) {}
}