JavaScript class中实现属性只读与私有保护需区分目标:#前缀定义真正私有字段(ES2022+,运行时强制),getter实现只读计算属性,推荐组合使用私有字段+只读getter构建安全接口。

JavaScript class 中实现属性只读与私有保护,核心是分清两个目标:**只读(read-only)解决“不能改”,私有(private)解决“看不见、访问不到”**。两者常配合使用,但机制不同、语法不同、适用场景也不同。
用 # 前缀定义真正私有字段
ES2022 起,# 是唯一原生、运行时强制的私有语法:
- 必须以 # 开头,如 #id、#token,否则报 SyntaxError
- 只能在 class 内部声明和访问,子类无法继承或读取父类的 # 字段
- 外部任何方式(
obj.#xxx、Reflect.get(obj, '#xxx')、obj['#xxx'])都会直接报错 - 可在 constructor 中初始化,也可在类体中直接声明:
class A { #count = 0; }
用 getter 实现只读计算属性
当某个值需对外暴露、但不允许修改,且可能依赖其他字段动态计算时,用 get 最自然:
- 写法:
get area() { return this.width * this.height; } - 调用:
rect.area(不加括号),像普通属性一样读取 - 赋值会静默失败(非严格模式)或抛
TypeError(严格模式) - 适合封装私有字段:
get name() { return this.#firstName + ' ' + this.#lastName; } - 避免副作用:不要在 getter 里发请求、改状态或触发复杂逻辑
组合使用:私有字段 + 只读 getter = 安全只读接口
这是最推荐的实践模式——把数据藏起来,把读取逻辑稳稳交出去:
- 敏感字段用 # 声明,确保外部不可见、不可碰
- 需要对外暴露的值,统一通过 getter 提供,天然只读
- 若需控制写入,可配对添加 setter(但 setter 仍只能操作内部 # 字段)
- 示例:
class User { #email; constructor(email) { this.#email = email; } get email() { return this.#email; } }
其他只读方案对比(不推荐用于 class 内部状态)
像 Object.defineProperty 或 Object.freeze 更适合普通对象或配置项,不太适合作为 class 成员的常规保护手段:
-
Object.defineProperty(this, 'x', { value: 1, writable: false }):能设单个只读属性,但无法限制删除或重定义(除非configurable: false),且写法冗长 -
Object.freeze(this):冻结整个实例,会阻止后续添加方法或属性,破坏 class 的可扩展性 - _ 前缀命名:只是约定,不是保护,外部仍可自由读写,仅作提示











