一、简介
Object.freeze() 是 JavaScript 提供的一个内置方法,用于冻结一个对象。冻结后的对象将变成**不可更改(immutable)**的:不能添加、删除或修改其属性。这个方法在需要创建只读配置对象、保护数据结构时非常有用。
const obj = { a: 1 };
Object.freeze(obj);
obj.a = 2; // 无效
obj.b = 3; // 无效
delete obj.a; // 无效
console.log(obj); // { a: 1 }
二、基本用法
语法:
Object.freeze(obj);
obj:要冻结的对象。- 返回值:冻结后的对象(与传入对象是同一个引用)。
示例:
const config = {
debug: true,
version: "1.0.0"
};
Object.freeze(config);
config.debug = false; // 不生效
config.newProp = "new"; // 不生效
delete config.version; // 不生效
三、冻结的实现机制
Object.freeze() 本质上是通过内部的 [[Configurable]] 和 [[Writable]] 属性控制对象的行为:
- 所有属性都变成不可写(writable: false)
- 所有属性都变成不可配置(configurable: false)
- 不能添加新属性
- 不能删除已有属性
冻结对象后,其行为相当于执行了:
Object.defineProperty(obj, 'prop', {
writable: false,
configurable: false
});
⚠️ 注意:冻结仅限于“第一层属性”,即 浅冻结(shallow freeze)。
深冻结(deep freeze)示例:
function deepFreeze(obj) {
Object.freeze(obj);
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key] !== null && !Object.isFrozen(obj[key])) {
deepFreeze(obj[key]);
}
}
return obj;
}
四、Object.freeze() 与 TypeScript 中 readonly 的关系
TypeScript 提供的 readonly 关键字也能使对象属性只读,但它和 Object.freeze() 有本质区别:
| 特性 | readonly(TypeScript) | Object.freeze()(JavaScript) |
|---|---|---|
| 作用时机 | 编译期类型检查 | 运行时行为 |
| 是否影响运行时对象结构 | ❌ 否 | ✅ 是 |
| 是否可以递归 | ✅ Readonly<T> 泛型支持递归 | ❌ 默认浅冻结 |
| 是否可以取消 | ✅ 可重新赋值绕过限制 | ❌ 不可逆 |
| 是否安全 | ⚠️ 只能防止开发者在 TypeScript 中误改 | ✅ 能防止运行时代码篡改 |
示例:readonly 类型检查(只影响编译时)
type Config = {
readonly debug: boolean;
};
const cfg: Config = { debug: true };
cfg.debug = false; // ❌ TypeScript 报错(但运行时仍然可修改)
编译后的 JavaScript 是什么样的?
const cfg: { readonly debug: boolean } = { debug: true };
会被编译为:
const cfg = { debug: true };
也就是说,readonly 不会生成任何运行时代码,只是类型约束。
什么时候用 readonly,什么时候用 freeze?
- 使用
readonly:- 在开发阶段防止误用(类型安全)
- 需要在类型系统中表达不变性,特别是在函数参数、接口定义中
- 使用
Object.freeze():- 在运行时防止对象被篡改,保障数据一致性
- 尤其适用于运行环境中存在非 TypeScript 代码的场景,或需要跨模块/框架传递配置
建议:
如果你使用 TypeScript,可以同时使用
readonly+Object.freeze(),提供开发阶段和运行时的双重保障。
const cfg = Object.freeze({
debug: true as const,
mode: "dev" as const,
});
五、使用注意事项
- 只能冻结对象类型(Object, Array, Function)
- 浅冻结:子对象依然可以被修改,如数组中的对象、嵌套属性等。
- 无法解冻:没有
Object.unfreeze(),一旦冻结不可撤销(只能手动创建副本)。 - 严格模式下会抛出错误:对冻结对象修改会在严格模式中抛出 TypeError。
- 性能影响:冻结对象可能对性能有一定影响,尤其在深度冻结时应谨慎使用。
六、常见用途
-
创建不可变配置对象:
const CONFIG = Object.freeze({ host: "localhost", port: 8080 }); -
防止函数参数被修改:
function process(options) { Object.freeze(options); // 确保 options 不被更改 } -
状态管理中保持数据不可变性(比如 Redux 状态树)
七、如何检测对象是否被冻结
使用 Object.isFrozen() 来检测一个对象是否被冻结:
const obj = {};
console.log(Object.isFrozen(obj)); // false
Object.freeze(obj);
console.log(Object.isFrozen(obj)); // true
八、小结
| 特性 | 描述 |
|---|---|
| 是否可修改属性值 | ❌ 否 |
| 是否可添加新属性 | ❌ 否 |
| 是否可删除属性 | ❌ 否 |
| 是否递归冻结 | ❌ 否(默认) |
| 是否可检测冻结状态 | ✅ Object.isFrozen() |
| 是否可撤销冻结 | ❌ 否 |