问题历史
在 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 保护敏感数据 – 直接访问时会导致漏洞在项目中,为存储密码在 User 类中使用了 private password 字段。一位开发者意外通过 user['password'] 进行调试,从而导致该字段被外部模块更改。
优点:
缺点:
开发者使用 ES 私有字段 #password。现在通过索引或在继承中访问该字段的尝试都不通过,即使使用外部库和工具,数据的安全性得到了保障。
优点:
缺点: