从封装到继承:深入理解 TypeScript 类中的 public、private、protected、static

在 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();               // 可以访问

分析:

usernamegreet 都属于对象实例,外部调用者可以直接访问,适合公开 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:静态,系统级别信息,无需实例即可访问。

总结

  1. public:实例成员,对外开放,适合对象 API。
  2. private:实例成员,仅类内部使用,保证数据安全。
  3. protected:实例成员,子类可访问,基类扩展点。
  4. static:类成员,属于类而非实例,适合共享资源或工具方法。

核心理解:

arduino 复制代码
访问控制(谁能访问) → public / protected / private
成员归属(谁拥有) → static vs 实例成员

这种方式可以帮助你在设计类时 明确每个成员的责任和访问边界,提高代码安全性和可维护性。

相关推荐
向日的葵0061 小时前
vue3路由的replace属性(四)
前端·javascript·vue.js·vue路由
杨超越luckly1 小时前
Agent应用指南:利用GET请求获取理想汽车门店位置信息
前端·python·html·汽车·可视化
阿猫的故乡1 小时前
Vue模板引用和组件暴露:ref拿DOM、defineExpose调方法,案例多到眼花
前端·javascript·vue.js
小雨下雨的雨7 小时前
井字棋AI机器人实现详解 - Minimax算法实战-鸿蒙PC Electron框架完成
前端·人工智能·算法·华为·electron·鸿蒙
ZC跨境爬虫11 小时前
跟着 MDN 学JavaScript day_7:数学运算与逻辑判断实战测试
开发语言·前端·javascript·学习·ecmascript
fangdengfu12311 小时前
ES分析系统各个服务日志占用量
java·前端·elasticsearch
JustHappy12 小时前
古法编程秘籍(六):程序到底是怎么跑起来的?从 IO 到中断,一次讲明白
前端·后端·全栈
HYCS13 小时前
用pixi.js实现fabric.js(六):从线性代数的角度理解编辑器交互
前端·javascript·canvas