1. 核心概念
Object.freeze() 是 JavaScript 中的一个内置方法,用于使对象变为不可变(immutable)状态,防止其属性被修改、添加或删除。
1.1 基本语法
Object.freeze(obj)
2. 功能特性
2.1 主要作用
| 特性 | 说明 | 示例 |
|---|---|---|
| 阻止属性修改 | 对象属性值不可更改 | obj.prop = 'new' 无效 |
| 阻止属性添加 | 无法添加新属性 | obj.newProp = 'value' 无效 |
| 阻止属性删除 | 无法删除属性 | delete obj.prop 无效 |
| 递归冻结 | 浅层冻结,不递归冻结嵌套对象 | obj.nested.value 仍可修改 |
2.2 返回值
- 返回原始对象本身(非副本)
- 冻结后的对象仍然是同一个对象引用
3. 使用示例
3.1 基础用法
const person = {
name: '张三',
age: 25,
city: '北京'
};
// 冻结对象
const frozenPerson = Object.freeze(person);
// 尝试修改属性 - 静默失败(严格模式下报错)
frozenPerson.name = '李四'; // 无效
frozenPerson.newProp = 'test'; // 无效
delete frozenPerson.age; // 无效
console.log(frozenPerson); // { name: '张三', age: 25, city: '北京' }
3.2 严格模式下的行为
'use strict';
const obj = { x: 42 };
Object.freeze(obj);
try {
obj.x = 9; // TypeError: Cannot assign to read only property 'x' of object
} catch (e) {
console.error(e.message);
}
3.3 浅层冻结特性
const config = {
apiUrl: 'https://api.example.com',
headers: {
'Content-Type': 'application/json'
}
};
Object.freeze(config);
// 顶层属性不可修改
config.apiUrl = 'https://new.api.com'; // 无效
// 但嵌套对象的内容仍可修改
config.headers['Content-Type'] = 'text/plain'; // 有效!
console.log(config.headers); // { 'Content-Type': 'text/plain' }
4. 相关方法对比
4.1 对象冻结方法对比
| 方法 | 作用 | 是否递归 | 示例 |
|---|---|---|---|
| Object.freeze() | 完全不可变 | 否(浅层) | Object.freeze(obj) |
| Object.seal() | 可修改现有属性,不可增删 | 否 | Object.seal(obj) |
| Object.preventExtensions() | 不可添加新属性,可修改/删除 | 否 | Object.preventExtensions(obj) |
4.2 方法对比表
| 特性 | freeze() | seal() | preventExtensions() |
|---|---|---|---|
| 修改属性值 | ❌ 禁止 | ✅ 允许 | ✅ 允许 |
| 添加新属性 | ❌ 禁止 | ❌ 禁止 | ❌ 禁止 |
| 删除属性 | ❌ 禁止 | ❌ 禁止 | ✅ 允许 |
| 检查是否可扩展 | Object.isFrozen() | Object.isSealed() | Object.isExtensible() |
5. 检测对象状态
5.1 检测方法
const obj = { a: 1 };
console.log(Object.isFrozen(obj)); // false
Object.freeze(obj);
console.log(Object.isFrozen(obj)); // true
const sealedObj = { b: 2 };
Object.seal(sealedObj);
console.log(Object.isSealed(sealedObj)); // true
console.log(Object.isExtensible(obj)); // false
6. 实际应用场景
6.1 配置对象保护
// 应用配置
const APP_CONFIG = Object.freeze({
API_BASE_URL: 'https://api.example.com',
TIMEOUT: 5000,
RETRY_COUNT: 3
});
// 防止意外修改
// APP_CONFIG.API_BASE_URL = 'hacked'; // 无效
6.2 常量定义
// 枚举值
const DIRECTION = Object.freeze({
UP: 'UP',
DOWN: 'DOWN',
LEFT: 'LEFT',
RIGHT: 'RIGHT'
});
// 使用
function move(direction) {
if (!Object.values(DIRECTION).includes(direction)) {
throw new Error('Invalid direction');
}
// ... 移动逻辑
}
6.3 函数参数保护
function processUser(user) {
// 冻结传入的对象,防止函数内部意外修改
const frozenUser = Object.freeze(user);
// 使用 frozenUser 进行操作
return {
...frozenUser,
processed: true
};
}
7. 注意事项
7.1 局限性
- 浅层冻结:只冻结对象本身,不递归冻结嵌套对象
- 无法冻结不可枚举属性:如
__proto__、constructor等 - 数组冻结:数组也是对象,同样适用
const arr = [1, 2, 3]; Object.freeze(arr); arr.push(4); // 无效 arr[0] = 10; // 无效
7.2 性能考虑
- 冻结操作本身有轻微性能开销
- 对于大型对象,考虑是否需要完全冻结
- 在频繁修改的对象上使用冻结会影响性能
8. 最佳实践
8.1 使用建议
- 顶层配置对象:使用
Object.freeze()保护全局配置 - 枚举值定义:创建不可变的枚举对象
- 函数参数保护:防止函数内部意外修改传入对象
- 状态管理:在 Redux 等状态管理库中保护状态对象
8.2 替代方案
// 使用 Proxy 实现更精细的控制
const createImmutable = (obj) => {
return new Proxy(obj, {
set() {
throw new Error('Object is immutable');
},
deleteProperty() {
throw new Error('Object is immutable');
}
});
};
9. 总结
Object.freeze() 是 JavaScript 中实现对象不可变性的重要工具,适用于:
- 保护配置对象不被意外修改
- 定义常量枚举值
- 在函数调用中保护参数对象
- 实现不可变状态管理
关键要点:
- 冻结是浅层的,不递归冻结嵌套对象
- 在严格模式下,修改冻结对象会抛出错误
- 使用
Object.isFrozen()可以检测对象是否被冻结 - 对于需要深度冻结的场景,需要递归调用
Object.freeze()