JavaScript无原生不可变类语法,但可通过Object.freeze()冻结实例、屏蔽可变操作、私有字段+只读getter、防御原型污染等策略模拟不可变派生行为。

JavaScript 本身没有原生的“不可变类”语法,但可以通过设计模式和语言特性模拟不可变类的派生行为——关键不是阻止继承,而是确保子类实例的状态在创建后无法被意外修改,同时保持继承链的完整性与可预测性。
用 Object.freeze() 封装实例状态
这是最直接的实践方式:在构造函数末尾冻结实例自身(不冻结原型),使属性不可写、不可配置、不可枚举。注意只冻结实例自有属性,不影响原型方法调用:
- 父类构造中初始化所有字段后立即 Object.freeze(this)
- 子类必须在调用
super()后、添加自有属性前完成冻结(否则新增属性会被冻结失效) - 若需支持深冻结(如嵌套对象/数组),需额外递归处理,但通常浅冻结已满足“不可变类”的核心语义
继承时禁用可变操作入口
不可变类的派生不是靠语法限制,而是靠契约约束。可在父类中主动屏蔽常见副作用操作:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 重写
set访问器,对已有属性赋值抛出错误(配合Object.defineProperty配合writable: false) - 对数组类字段(如
items)不暴露原始引用,只提供withItems(newItems)这类返回新实例的纯函数方法 - 子类扩展时,所有“修改”行为都应返回新实例(即实现 持久化数据结构 风格)
用 class + private 字段 + 只读 getter 构建安全基类
ES2022+ 支持私有字段(#value)和只读 getter,是构建不可变基类的现代基础:
立即学习“Java免费学习笔记(深入)”;
- 所有状态存为私有字段,外部无法直接访问或赋值
- 仅通过
get value()暴露只读访问,且 getter 不返回可变引用(例如对数组返回[...this.#items]) - 子类可通过
super调用父类构造,并在自己的私有字段上叠加逻辑,但无法篡改父类私有状态
避免原型污染与构造函数误用
不可变类派生容易在继承链中被绕过,需主动防御:
- 父类构造函数中检查
new.target,拒绝非 new 调用(防止状态泄漏到全局) - 不将可变对象(如空数组、字面量对象)作为原型属性,避免被子类实例共享修改
- 组合继承或寄生组合继承中,避免两次调用父构造函数导致重复初始化或冻结失败

















