访问修饰符
概述
访问修饰符(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),但不能降低 - 在类外部无法访问
private和protected成员
重要
- 访问修饰符是封装原则的重要体现,合理使用可以提高代码质量
- 私有成员应该通过公共方法访问,而不是直接暴露
- 受保护成员适合用于需要在子类中访问但不想对外暴露的场景
- 过度使用
public会破坏封装性,应该谨慎使用 - 访问修饰符的选择应该基于实际需求和设计原则
信息
访问修饰符的选择原则:
public:需要对外暴露的接口、API
- 类的公共接口
- 需要外部访问的属性和方法
protected:需要在子类中使用但不想对外暴露
- 子类需要重写的方法
- 子类需要访问的属性
- 模板方法模式中的钩子方法
private:内部实现细节
- 不应该暴露的内部状态
- 辅助方法
- 敏感数据(如密码、密钥)
实际应用场景:
- 数据封装:使用
private保护敏感数据 - 继承设计:使用
protected为子类提供扩展点 - API 设计:使用
public定义清晰的公共接口 - 内部实现:使用
private隐藏实现细节
相关链接
- 类基础 - 了解类的基础概念
- 类继承 - 学习类的继承机制
- 抽象类 - 了解抽象类的概念
- 接口 - 学习接口的定义和使用
- TypeScript 官方文档 - 访问修饰符