编程全栈开发者

私有字段(Private Fields)在 TypeScript 类中是如何工作的?使用 private 修饰符和 # 符号的私有属性有什么区别,它们的应用特点和限制是什么?

用 Hintsage AI 助手通过面试

答复。

问题历史

在 TypeScript 中,私有属性最初是通过 private 修饰符实现的,从 ECMAScript 2021 开始,JavaScript 开始支持真正的私有字段,使用 # 符号。主要目的是数据封装,以保护类的内部细节不被外部访问。

问题

TypeScript 中的 private 修饰符只在编译时确保私密性,但在编译后的 JavaScript 中,这些字段仍然可以通过直接访问对象属性来访问。这可能导致对象状态的意外改变。ES 私有字段(#)在类外部完全不可访问,即使在 JS 的运行时级别,也能真正保护数据。

解决方案

TypeScript 支持这两种方法。在选择它们之间时,应考虑所需的保护程度和与 JS 版本的兼容性限制。

代码示例:

class Example { private hidden: number; #trulyHidden: string; constructor() { this.hidden = 42; this.#trulyHidden = 'secret'; } getHidden() { return this.hidden; } getTrulyHidden() { return this.#trulyHidden; } } const x = new Example(); // x.hidden – TS 中错误,但在 JS 中通过 x['hidden'] 可以访问 // x.#trulyHidden – 即使在 JS 中也是语法错误

关键特点:

  • private 只在 TypeScript 编译阶段保证私密性
  • #field 在 JavaScript 执行层面提供真正的私密性
  • 使用 # 需要支持现代 JS(ES2021+)

陷阱问题。

是否可以通过直接访问在 JS 中访问 TypeScript 类的私有字段?

是的,可以,因为私密性仅在编译阶段实现。在原始 JS 中,该字段将成为对象的常规属性。例如:

class A { private x = 1; } const a = new A(); console.log((a as any)["x"]); // 1

是否可以在接口中使用 # 私有字段或通过 type 描述它们?

不可以,#private 字段仅是类实现的一部分,不能在接口、类型中描述或在类外部使用。接口只描述公共成员。

是否可以继承使用 # 声明的私有字段?

不可以,这些字段对继承的类完全不可用。只有类本身可以访问它们:

class Parent { #foo = 123; } class Child extends Parent { // this.#foo = 444; // 错误 }

类型错误和反模式

  • 使用 private 保护敏感数据 – 直接访问时会导致漏洞
  • 混淆类型层面的私密性和运行时的私密性
  • 尝试在旧版本 JS 中使用 #fields

生活中的示例

负面案例

在项目中,为存储密码在 User 类中使用了 private password 字段。一位开发者意外通过 user['password'] 进行调试,从而导致该字段被外部模块更改。

优点:

  • 与 TypeScript 的简单集成。

缺点:

  • 在 JS 运行时没有真正的保护
  • 易于意外绕过私密性

正面案例

开发者使用 ES 私有字段 #password。现在通过索引或在继承中访问该字段的尝试都不通过,即使使用外部库和工具,数据的安全性得到了保障。

优点:

  • 真实的私密性
  • 可靠的敏感属性保护

缺点:

  • 需要支持现代 JS(与 ES5 不兼容)
  • 私有字段在接口中不可见,在继承中无法重写