深层冻结嵌套常量对象需同时使用const声明和Object.freeze()递归冻结:const防重赋值,freeze防属性修改;单用const无法阻止对象内容变更,单用freeze默认仅浅冻结。

深层冻结一个嵌套的常量对象,核心是:先用 const 声明变量绑定(防重赋值),再用 Object.freeze() 递归冻结所有层级的对象(防属性修改)。两者缺一不可——const 不管内容,freeze 默认只冻一层。
为什么单靠 const 或 Object.freeze() 都不够
const 只锁定变量名指向的内存地址,不影响堆中对象内容;Object.freeze() 默认只做浅冻结,对嵌套对象、数组等内部值不生效。例如:
-
const user = { name: 'Alice', settings: { theme: 'dark' } };→user.settings.theme = 'light'依然成功 -
Object.freeze(user)→ 外层user.name冻住了,但user.settings还是可写的
手动实现深度冻结函数(推荐基础版)
遍历对象所有自有属性,对每个值为纯对象(非 null、非原始值、非函数)的属性递归调用 Object.freeze():
function deepFreeze(obj) {
if (obj !== null && typeof obj === 'object' && !Object.isFrozen(obj)) {
Object.freeze(obj);
Object.keys(obj).forEach(key => {
if (obj[key] !== null && typeof obj[key] === 'object') {
deepFreeze(obj[key]);
}
});
}
return obj;
}
使用方式:
立即学习“Java免费学习笔记(深入)”;
const config = deepFreeze({ api: 'https://x.com', auth: { token: 'abc' } });- 之后
config.auth.token = 'xyz'在严格模式下报错,非严格模式静默失败
注意边界情况和生产建议
真实项目中需额外处理几类常见问题:
- 跳过
null、undefined、原始值(string/number/boolean)、函数、Date、RegExp、Map、Set 等非普通对象 - 避免循环引用导致栈溢出:可用
WeakMap缓存已处理对象进行判重 - 数组也属于 object 类型,会被递归冻结(
push/pop失效),如需保留数组方法行为,应单独判断并跳过或特殊处理 - 冻结后无法解冻,且仅适用于真正不变的配置、枚举、Schema 等极少数场景,别滥用在频繁更新的状态上
替代方案与工具参考
若项目已用现代构建链路,可考虑:
- 使用
immer+produce创建不可变更新逻辑,而非硬冻结 - TypeScript 配合
readonly修饰符 +as const字面量断言,在编译期约束结构(运行时仍需 freeze 保证) - 第三方库如
deep-freeze(注意其未处理循环引用,慎用于复杂数据)


















