const声明对象仅防重新赋值,深度只读需Proxy递归代理并拦截set/deleteProperty/defineProperty等操作,对引用类型自动再代理,Map/Set等内置对象需额外拦截其变更方法。

用 const 声明对象只能防止变量重新赋值,不能阻止对象内部属性被修改。要实现真正的深度只读(包括嵌套对象、数组、Map、Set 等),需结合 Proxy 手动拦截所有可变操作。
核心思路:递归代理 + 全面拦截
Proxy 本身不自动递归,必须在 get 拦截中对返回的引用类型(对象、数组等)再次包装为只读 Proxy;同时在 set、deleteProperty、defineProperty 等陷阱中一律返回 false 或抛错。
基础只读 Proxy 实现
以下是一个轻量、可复用的深度只读封装函数:
function deepFreeze(obj) {
if (obj === null || typeof obj !== 'object') return obj;
// 已代理过,直接返回(避免重复代理)
if (obj[Symbol.for('isFrozen')]) return obj;
const handler = {
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
// 对引用类型递归代理,基本类型直接返回
if (value && typeof value === 'object') {
return deepFreeze(value);
}
return value;
},
set() { return false; }, // 静默失败(严格模式下会报错)
deleteProperty() { return false; },
defineProperty() { return false; },
setPrototypeOf() { return false; },
preventExtensions() { return false; },
ownKeys(target) {
// 允许枚举,但禁止新增/删除
return Reflect.ownKeys(target);
}
};
const proxy = new Proxy(obj, handler);
proxy[Symbol.for('isFrozen')] = true; // 标记已冻结
return proxy;
}
处理特殊内置对象(Map/Set/Date/RegExp 等)
原生 Map、Set 等无法通过普通 get 拦截其方法(如 map.set()),需额外包装或限制访问:
阿里百炼大模型平台多人共享账号用量统计代理服务。解决公司共用账号时无法区分个人用量的问题。适用场景:1)部署用量统计代理;2)统计多人Token用量和调用次数;3)管理大模型API调用配额。
立即学习“Java免费学习笔记(深入)”;
- 对
Map/Set实例,在get中拦截其变更方法(set、delete、clear等),统一返回只读版本或抛错 - 对
Date、RegExp等不可变原始包装对象,通常无需代理(它们本就不允许内部状态修改) - 若需完全禁用,可在
get中检测方法名并返回undefined或 throw
使用示例与注意事项
✅ 正确用法:
const data = deepFreeze({
name: 'Alice',
profile: {
age: 30,
tags: ['dev', 'js']
},
scores: new Map([['math', 95]])
});
data.name = 'Bob'; // 无效(静默)
data.profile.age = 31; // 无效
data.profile.tags.push('ts'); // 无效(数组方法也被拦截)
data.scores.set('eng', 90); // 无效(Map 方法被拦截)
⚠️ 注意事项:
- Proxy 只拦截“外部”操作,不改变对象原型行为;若对象有自定义 setter,需确保它们也遵循只读逻辑
- 性能开销存在,避免对超大嵌套结构高频使用
- JSON 序列化仍正常(
JSON.stringify不触发 Proxy 的 set/delete) - 不兼容 IE,现代浏览器均可支持

















