object.freeze仅对对象生效,无法保护原始类型;原始类型值不可变但变量可重赋值,需const声明防重赋值,object.freeze防对象属性修改,深层冻结需递归实现。

Object.freeze 不能直接保护原始类型常量,因为它只对对象生效;原始类型(如 string、number、boolean)本身不可变,但变量仍可被重新赋值。真正的“安全保护”需结合 const 声明与 Object.freeze 的组合策略,重点在于冻结对象结构,而非原始值本身。
原始类型天然不可变,但变量引用可变
JavaScript 中的原始类型(string、number、bigint、boolean、null、undefined、symbol)在值层面不可修改——你无法改变一个字符串的内容,比如 "hello".toUpperCase() 返回新字符串,原值不变。但变量本身可以被重新赋值:
const PI = 3.14; PI = 3.14159; // TypeError: Assignment to constant variable(严格模式下)
注意:这里报错靠的是 const,不是 Object.freeze。Object.freeze 对原始值调用会直接返回该原始值,不产生冻结效果:
Object.freeze(42); // 返回 42,无任何防护作用 Object.isFrozen(42); // false(因为原始值不是对象)
用 Object.freeze 保护常量对象(如枚举、配置集合)
当需要定义一组命名常量(例如状态码、颜色主题、API 端点)时,应使用对象封装,并用 Object.freeze 防止属性被增删改:
- 用 const 声明变量,防止变量重赋值
- 用 Object.freeze 冻结对象,防止属性被修改、添加或删除
- 推荐配合 Object.defineProperty 或 Object.freeze(Object.assign({}, ...)) 构建不可变结构
const HTTP_STATUS = Object.freeze({
OK: 200,
NOT_FOUND: 404,
SERVER_ERROR: 500
});
HTTP_STATUS.OK = 201; // 无效(静默失败,非严格模式下)
delete HTTP_STATUS.OK; // false(严格模式下抛 TypeError)
深层冻结需递归处理(浅冻结的局限)
Object.freeze 只冻结对象**自身属性**,不递归冻结嵌套对象。若常量包含子对象,需手动深冻结:
- 简单场景:避免在常量对象中嵌套可变对象
- 必要时:用递归函数或第三方库(如 immer 的 freeze)确保完整不可变
- 更稳妥方式:用 class + private fields + getter 或 Symbol-keyed constants 替代深度嵌套
function deepFreeze(obj) {
if (obj !== null && typeof obj === 'object') {
Object.getOwnPropertyNames(obj).forEach(prop => {
deepFreeze(obj[prop]);
});
}
return Object.freeze(obj);
}
const THEME = deepFreeze({
colors: { primary: '#007bff', text: '#333' },
spacing: { small: 4, large: 16 }
});
现代替代方案:更清晰的常量表达
对于纯原始值常量,优先使用 const;对于结构化常量,可考虑:
- ES6 模块导出常量对象:模块顶层 const + freeze,天然具备单例和不可重赋值特性
-
Typescript 枚举或 const assertion:
as const让编译器推断字面量类型,配合 freeze 提供运行时+类型双重保障 -
Map 或 Set 封装键值常量:用
Object.freeze(new Map([['KEY', 'value']])),但注意 Map 实例本身仍可操作,需包装成只读接口











