普通视图

发现新文章,点击刷新页面。
今天 — 2025年11月30日首页

TS和JS成员变量修饰符

作者 Robet
2025年11月30日 09:28

在 TypeScript 和 JavaScript 中,类成员变量(属性)的修饰符(Modifiers) 用于控制其可见性、可访问性和可变性。两者在能力上有显著差异:TypeScript 提供了更丰富的编译时修饰符,而 JavaScript(ES2022 起)引入了运行时私有字段

下面从 TypeScriptJavaScript 两个角度分别说明,并对比异同。


一、TypeScript 类成员变量修饰符(编译时)

TypeScript 在 编译阶段 提供以下关键字作为访问修饰符:

修饰符 含义 是否生成 JS 代码 可见性
public 公共(默认) ❌ 不生成额外代码 类内、子类、外部均可访问
private 私有 ❌ 仅类型检查,不阻止运行时访问 仅类内部可访问(TS 编译时报错)
protected 受保护 ❌ 仅类型检查 类内部 + 子类可访问
readonly 只读 ❌ 仅类型检查 初始化后不可修改(TS 报错)

✅ 示例(TypeScript):

class User {
  public name: string;        // 默认就是 public
  private id: number;         // TS 禁止外部访问
  protected email: string;    // 子类可访问
  readonly createdAt: Date;   // 初始化后不可改

  constructor(name: string, id: number, email: string) {
    this.name = name;
    this.id = id;
    this.email = email;
    this.createdAt = new Date();
  }
}

⚠️ 注意:
private / protected 只在 TypeScript 编译时生效,编译成 JS 后,这些字段仍是普通属性,运行时仍可被访问或修改

// 编译后的 JS(无 private 保护!)
const user = new User("Alice", 1, "a@example.com");
console.log(user.id); // ✅ 能访问!JS 不报错
user.id = 999;        // ✅ 能修改!

二、JavaScript 类成员变量修饰符(运行时,ES2022+)

ECMAScript 2022(ES13) 开始,JavaScript 原生支持 真正的私有字段(Private Fields) ,使用 # 前缀。

语法 含义 运行时是否私有 是否可被外部访问
#fieldName 私有字段 ✅ 是 ❌ 完全无法从类外访问
普通字段(无前缀) 公共字段 ❌ 否 ✅ 可自由访问

✅ 示例(JavaScript / TypeScript 均支持):

class User {
  #id;                    // 私有字段(JS 原生私有)
  name;                   // 公共字段

  constructor(name, id) {
    this.name = name;
    this.#id = id;        // 只能在类内部访问
  }

  getId() {
    return this.#id;      // ✅ OK
  }
}

const user = new User("Bob", 2);
console.log(user.name);   // ✅ "Bob"
console.log(user.#id);    // ❌ SyntaxError! 无法访问

关键优势
#id真正的私有,即使在运行时也无法绕过(除非用 Proxy 等 hack,但正常代码做不到)。


三、TypeScript 对 JS 私有字段的支持

TypeScript 完全支持 # 私有字段,并提供类型检查:

class User {
  #id: number;
  name: string;

  constructor(name: string, id: number) {
    this.name = name;
    this.#id = id;
  }

  getId(): number {
    return this.#id; // ✅ TS 知道这是 number
  }
}

🔸 此时你不需要private,因为 #id 已经是运行时私有。


四、对比总结

特性 TypeScript private JavaScript #field
作用时机 编译时(类型检查) 运行时(真实私有)
能否被外部访问 ✅ 能(JS 无保护) ❌ 不能
是否生成额外代码 ❌ 否 ✅ 是(保留 # 语法)
兼容性 所有 JS 环境(因被擦除) 需要 ES2022+ 或 Babel 转译
推荐场景 快速开发、内部项目 需要真正封装、库开发

五、最佳实践建议

✅ 优先使用 JavaScript 原生私有字段 #

  • 如果目标环境支持(现代浏览器 / Node.js 12+),优先用 #fieldName
  • 它提供真正的封装,避免“假装私有”的陷阱。

✅ 在 TypeScript 中:

  • 若需兼容旧环境 → 用 private(但要清楚它只是“纸面私有”)。
  • 若用现代环境 → 直接用 # ,无需 private

✅ 不要混用:

// ❌ 不推荐:语义重复且混乱
private #id; // 错误!不能同时用

readonly 仍是 TS 特有(JS 无等价物)

  • 可配合 # 使用:

    class Config {
      readonly #apiUrl: string;
      constructor(url: string) {
        this.#apiUrl = url; // 初始化后不可变(TS 检查)
      }
    }
    

六、补充:其他相关修饰符

修饰符 语言 说明
static TS & JS 静态成员(属于类,不属于实例)
abstract TS only 抽象类/方法(不能实例化)
declare TS only 声明属性存在(用于 .d.ts 或装饰器)

✅ 总结

需求 推荐方案
真正的私有字段 使用 JavaScript #fieldName(ES2022+)
仅开发时提醒(兼容旧环境) 使用 TypeScript private
只读属性 TypeScript readonly(JS 无原生支持)
公共字段 直接声明(TS/JS 均默认 public)

🎯 现代项目建议
# 实现私有,用 readonly 实现只读,放弃 private(除非必须兼容旧 JS)

这样既能获得类型安全,又能保证运行时封装性。

❌
❌