Object.defineProperties本身不提供运行时锁定,而是通过writable和configurable设为false实现属性值与结构双锁定,配合preventExtensions、seal或freeze阻断扩展与原型污染,并需一次性连贯执行以确保原子化。

Object.defineProperties 本身不提供运行时的“锁定”能力,它只是在定义属性时设置描述符(如 writable、configurable、enumerable),属于静态元数据控制。所谓“底层原子化锁定”,实际是指:在对象初始化阶段,通过精确配置属性描述符 + 合理封装访问逻辑,使业务对象的关键属性具备不可篡改、不可重定义、不可枚举等强约束行为,从而在语义层面达成“变更隔离”与“状态固化”效果。
用 writable: false 和 configurable: false 实现属性值与结构双锁定
这是最直接的原子化控制手段。一旦将属性设为 writable: false,任何赋值操作(包括隐式转换)都将静默失败(严格模式下抛 TypeError);设为 configurable: false 后,该属性无法被 delete、无法修改描述符、无法重新定义。
- 必须在对象创建后立即一次性定义所有锁定属性,不能分批调用
defineProperties——否则中间状态可能被篡改 - 避免使用
Object.assign或展开运算符初始化,它们会绕过描述符,生成默认可写的属性 - 示例:
const order = {}; Object.defineProperties(order, { id: { value: 'ORD-2024-001', writable: false, configurable: false, enumerable: true }, status: { value: 'pending', writable: false, configurable: false, enumerable: true }, createdAt: { value: new Date(), writable: false, configurable: false, enumerable: false } });
结合 get/set 描述符实现受控变更入口
若需允许有限度的变更(如仅允许状态机流转),可将属性定义为 accessor 属性,并在 set 中嵌入业务规则校验。此时 writable 和 value 不再生效,configurable: false 仍能防止描述符被覆盖。
- 私有存储建议用
WeakMap隔离,避免暴露内部字段 -
set函数中 throw 错误是关键:它让非法赋值立刻中断,而非静默忽略 - 示例:
const _state = new WeakMap(); const validStatuses = ['draft', 'confirmed', 'shipped', 'cancelled']; const order = {}; _state.set(order, { status: 'draft' }); Object.defineProperties(order, { status: { get() { return _state.get(this).status; }, set(value) { if (!validStatuses.includes(value)) throw new Error(`Invalid status: ${value}`); if (this.status === 'cancelled') throw new Error('Cannot change status after cancellation'); _state.get(this).status = value; }, configurable: false, enumerable: true } });
冻结原型链 + 禁止扩展,杜绝意外污染
单靠属性级锁定不够,还需阻断对象自身结构扩展和原型污染路径:
- 调用
Object.preventExtensions(obj)禁止新增属性 - 调用
Object.seal(obj)(等价于 preventExtensions + 所有现有属性configurable: false) - 调用
Object.freeze(obj)(seal + 所有数据属性writable: false)——但注意它不递归冻结嵌套对象 - 若业务对象含子对象(如
order.items),需手动递归冻结或用Object.freeze+ 自定义深冻结工具
配合 Object.getOwnPropertyDescriptors 做安全克隆与审计
当需要基于原对象创建新实例(如状态快照、版本回滚),应避免浅拷贝破坏锁定状态:
- 用
Object.getOwnPropertyDescriptors(obj)获取完整描述符,再传给Object.defineProperties创建新对象,确保锁定策略完全继承 - 可封装一个
lockObject工具函数,统一执行 defineProperties + seal/freeze + 原型冻结,降低出错概率 - 对关键业务对象,在初始化后用
Object.isFrozen()或自检描述符,作为上线前的轻量验证
不复杂但容易忽略:真正的“原子化”不在单次操作,而在整个初始化流程的不可分割性——从空对象创建、到描述符定义、再到防扩展/冻结,必须连贯执行且无中间可写窗口。只要其中一环松动,锁定就形同虚设。

















