Object.freeze()是构建不可变配置对象的首选,可阻止增删属性、修改描述符及已有属性值;需在赋值完成后立即调用,且仅作用于自身层级,嵌套对象须递归冻结。
要构建不可变的配置对象,object.freeze() 是首选,object.seal() 只能作为轻量级限制手段,不能替代冻结——它允许修改已有属性值,而配置通常要求值也不可变。
用 Object.freeze() 确保完全不可变
Object.freeze() 会阻止添加、删除属性,禁止重配置(如 writable、configurable),同时让所有现有属性变为只读。这是配置对象“真正不可变”的核心保障。
- 必须在对象创建并赋值完成后立即调用,否则后续修改可能已发生
- 仅作用于对象**自身层级**,嵌套对象仍可变;需递归冻结或使用结构化克隆+冻结(如配合
structuredClone) - 冻结后对属性赋值在严格模式下抛错,非严格模式静默失败,建议始终启用严格模式
示例:
'use strict';
const config = {
API_URL: 'https://api.example.com',
TIMEOUT: 5000,
FEATURES: {
analytics: true,
darkMode: false
}
};
// 冻结顶层
Object.freeze(config);
// ✅ 安全:无法新增、删除、重定义
// config.NEW_KEY = 'test'; // TypeError(严格模式)
// delete config.TIMEOUT; // false(非严格)或 TypeError
// Object.defineProperty(config, 'API_URL', { writable: false }); // TypeError
// ⚠️ 注意:嵌套对象未冻结,仍可改
config.FEATURES.analytics = false; // 成功!
递归冻结嵌套配置(深冻结)
多数配置含嵌套结构,需手动或工具辅助深冻结。简单递归实现即可覆盖常见场景:
- 检查属性是否为纯对象(排除
null、数组、Date、RegExp 等) - 对每个对象属性递归调用深冻结函数,再冻结父对象
- 避免循环引用(生产环境建议用库如
deep-freeze或immer的 freeze 工具)
简易深冻结函数:
function deepFreeze(obj) {
if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
Object.getOwnPropertyNames(obj).forEach(prop => {
deepFreeze(obj[prop]);
});
return Object.freeze(obj);
}
return obj;
}
const config = { DB: { host: 'localhost', port: 5432 }, LOG_LEVEL: 'INFO' };
deepFreeze(config);
config.DB.port = 5433; // TypeError
Object.seal() 的适用边界:仅防结构变更
Object.seal() 仅禁止增删属性和修改属性描述符,但允许修改已有属性的值。因此它**不适合配置对象**,除非你明确接受“键固定、值可变”这一语义(例如运行时可热更新的开关)。
- 适合临时封存对象结构,比如函数参数校验后防止意外篡改 shape
- 性能略优于
freeze(不处理writable标志),但差异微乎其微 - 与
freeze不互斥:先seal再freeze无意义,直接freeze更彻底
不推荐用于配置:
const config = { MODE: 'prod' };
Object.seal(config);
config.MODE = 'dev'; // ✅ 允许 —— 配置值被意外改写了
config.DEBUG = true; // ❌ 报错 —— 但值已不可信
配合 const 与模块封装增强安全性
const 声明仅防止变量重新赋值,不保护对象内容;需与 Object.freeze() 配合,并通过模块默认导出隐藏引用:
- 在单独配置模块中创建、冻结、导出,避免外部直接构造或修改
- 不导出原始对象,而是导出访问器函数或冻结后的副本
- 搭配 TypeScript 的
readonly类型提示,从开发阶段强化不可变意识
模块写法示例:
// config.js
const rawConfig = {
BASE_URL: import.meta.env.BASE_URL || '/',
IS_PROD: import.meta.env.PROD
};
export const CONFIG = Object.freeze(rawConfig);
// 使用处
import { CONFIG } from './config.js';
// CONFIG.BASE_URL = '/new'; // TypeError

















