object.preventextensions 配合属性描述符可冻结动态表单字段拓扑:先禁扩展,再逐字段设 writable: false 和 configurable: false;对嵌套对象需递归冻结,区别于 object.freeze 的全量锁定,适用于分阶段字段注册场景。

用 Object.preventExtensions 配合属性描述符(如 writable: false、configurable: false),可以有效冻结动态表单的字段结构——即禁止新增字段,同时锁定已有字段的可写性与可配置性,防止运行时意外篡改拓扑。
先冻结对象扩展性,再逐字段锁定描述符
仅调用 Object.preventExtensions(obj) 只能阻止新增属性,但已有字段仍可被重新赋值、删除或修改描述符。必须配合 Object.defineProperty 或 Object.defineProperties 显式设置每个字段的描述符:
-
设置
writable: false:禁止修改字段值(对原始值生效;对引用类型需额外冻结其内容) -
设置
configurable: false:禁止删除该字段,也不允许再次修改其描述符(包括writable) -
若字段值是对象,需递归调用
Object.freeze或Object.preventExtensions,否则内部属性仍可被修改
适用于表单字段拓扑的典型场景
动态表单常通过配置生成字段(如 fields = [{ key: 'email', type: 'input' }, ...]),最终映射为数据对象(如 { email: '', name: '' })。锁定拓扑的关键时机是表单初始化完成、字段注册完毕后:
- 在表单实例化末尾统一调用锁定逻辑,避免后续任意代码增删字段(例如插件误操作、调试代码污染)
- 对每个字段名执行
Object.defineProperty(data, key, { writable: false, configurable: false }) - 若字段值支持嵌套(如
address: { city: '', zip: '' }),先Object.freeze(address)再定义描述符,确保深层不可变
注意与 Object.freeze 的区别
Object.freeze(obj) 实际上等价于 Object.preventExtensions(obj) + 所有自有属性设为 <code>configurable: false, writable: false。但它会一次性处理所有现有属性,不适用于“分阶段定义字段后锁定”的动态场景:
- 若表单字段是逐步添加(如条件分支加载不同字段),应在全部添加完后再调用
freeze,否则中途添加的字段不会被冻结 - 更灵活的做法是:每添加一个字段,就立即定义其不可写不可配置;最后再
preventExtensions—— 这样即使中途出错,已注册字段也已受保护 - 若需保留某些字段可编辑(如只读字段 vs 编辑字段),就不能全量
freeze,而应按需设置各字段描述符
验证锁定是否生效的小技巧
开发期可用简单断言快速验证:
-
Object.isExtensible(formState) === false确认无法新增字段 -
Object.getOwnPropertyDescriptor(formState, 'email')?.writable === false检查字段是否只读 - 尝试
formState.newField = 'test'或delete formState.email,应静默失败(严格模式下抛TypeError)
不复杂但容易忽略:拓扑锁定不是一劳永逸,它依赖你在字段定义完成后的“那一帧”及时执行。延迟或遗漏,就留下可被篡改的窗口。











