直接返回 Object.freeze 封装的对象可固化状态,防止后续修改;冻结仅作用于自身属性,嵌套对象需手动深冻结,适用于配置项、常量等不可变场景。

直接返回一个被 Object.freeze 封装后的对象,就能在函数执行结束时锁定状态,防止后续意外修改——关键不是“中断执行”,而是“执行完毕后立即冻结”。
return 与 Object.freeze 的配合本质
return 负责把最终状态作为值输出,Object.freeze 负责切断该对象的可变性。二者结合,是在构造完成的那一刻就固化结构和属性值,而不是靠运行时拦截或代理。
- 冻结只作用于对象自身属性(不递归),若含嵌套对象,需手动逐层冻结或使用深冻结工具
- 冻结后无法添加、删除、重配置属性,也无法修改已有数据属性的值(但引用类型内部仍可变)
- 必须在对象构造完成、所有初始化逻辑走完之后调用
Object.freeze,再 return
典型安全构造模式
适合用于创建配置项、常量枚举、不可变初始状态等场景。例如封装一个带校验的只读用户配置:
function createUserConfig(name, role) {
if (!name || !role) throw new Error('name and role required');
const config = {
name: String(name).trim(),
role: String(role).toUpperCase(),
createdAt: new Date().toISOString(),
isValid: true
};
return Object.freeze(config);
}
调用后得到的对象任何属性都无法被覆盖或删除,连 config.name = 'hacker' 都静默失败(严格模式下会抛错)。
注意深冻结陷阱
如果状态中包含数组、日期、嵌套对象等引用类型,Object.freeze 不会自动冻结它们内部:
-
Object.freeze({ list: [1,2,3] })→list.push(4)依然生效 - 需要显式冻结子结构,或使用递归冻结函数(如
deepFreeze) - 更稳妥的做法是:用
Object.freeze+Object.seal+ 不可变数据结构(如Array.from创建新数组)组合防御
与 class 或原型链的兼容写法
若想让实例具备冻结后不可篡改的原型行为,可在构造函数末尾冻结 this,或返回冻结后的 plain object(推荐):
class ImmutableUser {
constructor(name, email) {
Object.assign(this, { name, email, id: Math.random().toString(36).slice(2, 10) });
Object.freeze(this); // 冻结实例本身(不推荐在 class 内部做,影响继承调试)
}
}
// 更推荐:工厂函数 + freeze + 纯对象
const makeUser = (name, email) => Object.freeze({
name,
email,
id: crypto.randomUUID?.() ?? Date.now().toString(36)
});
这样既避免 class 实例方法被意外覆盖,又确保每个生成对象从诞生起就是只读契约。


















