在 TypeScript 中,类(Class)是面向对象编程的核心。对于类的成员,我们经常会遇到四个关键修饰符:
public:公开的成员,任何地方都能访问。private:私有成员,仅当前类可访问。protected:受保护成员,当前类和子类可访问。static:静态成员,属于类本身而非实例。
正确理解这些修饰符,不只是为了应付面试,而是为了在实际项目中写出 可维护、可扩展、安全 的代码。
一、public:面向外部 API 的成员
使用场景:
- 任何类实例都需要访问的属性或方法。
- 需要作为对象的"接口"对外暴露。
示例 1:用户信息
typescript
class User {
public username: string;
constructor(username: string) {
this.username = username;
}
public greet() {
console.log(`Hello, ${this.username}`);
}
}
const u = new User("Tom");
console.log(u.username); // 可以访问
u.greet(); // 可以访问
分析:
username 和 greet 都属于对象实例,外部调用者可以直接访问,适合公开 API。
二、private:隐藏内部实现,保证安全
使用场景:
- 敏感数据(密码、Token)。
- 内部状态(缓存、计数器)。
- 内部方法(辅助函数)不希望外部调用。
示例 2:账户密码管理
typescript
class BankAccount {
private balance: number;
constructor(initial: number) {
this.balance = initial;
}
deposit(amount: number) {
if (amount > 0) this.balance += amount;
}
withdraw(amount: number) {
if (amount > 0 && this.balance >= amount) this.balance -= amount;
}
public getBalance() {
return this.balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.balance); // ❌ 报错,私有成员不可访问
分析:
balance 是内部状态,外部无法直接修改,只能通过方法操作,保证了安全性和数据完整性。
三、protected:对子类开放的成员
使用场景:
- 框架或基类中提供的"扩展点"。
- 允许子类继承和修改,但不希望外部直接访问。
示例 3:游戏对象坐标
typescript
class GameObject {
protected x: number = 0;
protected y: number = 0;
protected move(dx: number, dy: number) {
this.x += dx;
this.y += dy;
}
}
class Player extends GameObject {
moveUp() {
this.move(0, 1); // 子类可以访问 protected 方法
}
getPosition() {
return { x: this.x, y: this.y }; // 子类可以访问 protected 属性
}
}
const p = new Player();
p.moveUp();
console.log(p.getPosition()); // { x:0, y:1 }
// p.x; // ❌ 外部不可访问
分析:
protected 成员提供了继承体系中的能力扩展,同时防止外部随意操作内部状态,适合设计可扩展的基类。
四、static:类级共享资源
使用场景:
- 系统配置、常量。
- 工具类方法。
- 只需一份、无需每个实例重复保存的数据。
示例 4:系统版本和工具方法
typescript
class System {
static version = "1.0";
static log(msg: string) {
console.log(`[System v${System.version}] ${msg}`);
}
}
// 访问方式:类本身
console.log(System.version); // 1.0
System.log("启动成功"); // [System v1.0] 启动成功
// ❌ 实例无法访问
// const s = new System();
// s.log("test"); // 报错
分析:
静态成员属于类而非实例,节省内存,同时明确表示这些方法和数据与对象实例无关。
五、public vs static 对比
| 特性 | public | static |
|---|---|---|
| 归属 | 实例对象 | 类本身 |
| 调用方式 | 通过对象实例 obj.member |
通过类 Class.member |
| 内存占用 | 每个实例都有一份 | 只有一份 |
| 使用场景 | 对象状态、对象方法 | 系统全局状态、工具方法、常量 |
示例对比:
typescript
class User {
public username: string; // 实例成员
static platform = "Web"; // 类成员
constructor(name: string) {
this.username = name;
}
}
const u1 = new User("Tom");
const u2 = new User("Lucy");
console.log(u1.username, u2.username); // Tom Lucy
console.log(User.platform); // Web
六、protected vs private 对比
| 特性 | protected | private |
|---|---|---|
| 访问权限 | 当前类 + 子类 | 当前类 |
| 外部访问 | ❌ | ❌ |
| 使用场景 | 可扩展的基类成员 | 内部状态或辅助方法 |
示例对比:
scala
class Animal {
protected species: string = "Unknown";
private dna: string = "XYZ";
}
class Dog extends Animal {
printInfo() {
console.log(this.species); // ✅ 可访问 protected
// console.log(this.dna); // ❌ 报错 private
}
}
七、实际项目应用示例
typescript
interface LoginService {
login(username: string, password: string): boolean;
}
class UserService implements LoginService {
public currentUser: string | null = null;
protected role: string = "user";
private passwordHash: string = "";
static version = "1.0";
login(username: string, password: string): boolean {
if (this.validatePassword(password)) {
this.currentUser = username;
console.log(`用户 ${username} 登录成功`);
return true;
}
return false;
}
private validatePassword(password: string): boolean {
// 模拟加密验证
this.passwordHash = password.split("").reverse().join("");
return password.length >= 6;
}
}
console.log(UserService.version); // 1.0
const service = new UserService();
service.login("Tom", "123456");
console.log(service.currentUser); // Tom
// console.log(service.passwordHash); // ❌ 私有
// console.log(service.role); // ❌ 受保护
分析:
currentUser:公开状态,允许外部访问和展示。role:受保护,只能在子类扩展时访问。passwordHash:私有,防止外部直接操作。version:静态,系统级别信息,无需实例即可访问。
总结
- public:实例成员,对外开放,适合对象 API。
- private:实例成员,仅类内部使用,保证数据安全。
- protected:实例成员,子类可访问,基类扩展点。
- static:类成员,属于类而非实例,适合共享资源或工具方法。
核心理解:
arduino
访问控制(谁能访问) → public / protected / private
成员归属(谁拥有) → static vs 实例成员
这种方式可以帮助你在设计类时 明确每个成员的责任和访问边界,提高代码安全性和可维护性。