属性描述符通过writable和configurable控制属性不可变性,两者均为false时实现常量属性;object.freeze批量设置二者为false以冻结对象自身;enumerable:false隐藏属性;访问器描述符则支持带校验的伪不可变。

属性描述符是 JavaScript 中构建不可变数据结构的核心控制机制,它不改变值本身,而是通过限制对属性的访问和修改方式,从行为层面实现“不可变”语义。
用 writable 和 configurable 组合实现不同层级的不可变性
单个属性的不可变程度取决于两个关键描述符的组合:
- writable: false 阻止赋值操作,但属性仍可被删除或重新定义(若 configurable 为 true)
- configurable: false 锁定属性结构:禁止删除、禁止修改描述符(包括 writable/enumerable)、禁止转为访问器属性
- 两者同时设为 false,才能创建真正意义上的“常量属性”——既不能改值,也不能删或重定义
例如:Object.defineProperty(obj, 'id', { value: 123, writable: false, configurable: false }) 后,obj.id = 456 和 delete obj.id 均失效(严格模式下抛错)。
配合 Object.freeze 实现对象级冻结
Object.freeze() 并非黑盒,它的底层正是批量应用属性描述符:
- 先调用
Object.seal()(即对所有自有属性设configurable: false) - 再对所有数据属性设
writable: false - 结果是:属性值不可改、属性不可删、不可新增、不可重配置
注意:freeze 只作用于对象自身,不递归冻结嵌套对象;如需深度冻结,需手动遍历或借助库。
隐藏内部状态,避免意外篡改
不可变不仅指“不能改”,也包括“不该被看到”。enumerable: false 是关键辅助手段:
- 设为 false 后,该属性不会出现在
for...in、Object.keys()、JSON.stringify()中 - 但仍可通过点号或方括号直接访问和读写(除非同时设
writable: false) - 适合存放缓存、元信息、临时计算结果等不希望暴露给外部遍历逻辑的字段
例如:为类实例添加私有缓存字段 _computedValue,设 enumerable: false 可防止它被序列化或意外遍历到。
访问器描述符支持受控的“伪不可变”
当需要在保持接口不变的前提下加入校验或转换逻辑时,get/set 提供了更灵活的不可变路径:
-
get可返回派生值、格式化结果或只读缓存,无需暴露原始字段 -
set可拦截赋值,做类型检查、范围验证,甚至静默拒绝非法输入 - 相比纯数据描述符,访问器能实现“逻辑上不可变”,比如只允许设置一次、或仅在初始化阶段写入
这种模式常见于配置对象或领域模型中,既保障数据一致性,又保留运行时干预能力。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











