JavaScript中用Proxy实现对象只读保护的核心是拦截set、deleteProperty和defineProperty等操作并拒绝修改,通过代理层控制访问行为,不改变原对象;深度只读需在get中递归代理嵌套对象,注意循环引用和兼容性。

JavaScript 中用 Proxy 实现对象只读保护,核心是拦截赋值(set)、删除(deleteProperty)和定义新属性(defineProperty)操作,并拒绝修改。它不改变原对象,而是创建一个“代理层”来控制访问行为。
拦截关键操作阻止写入
只读的关键不是让属性不可读,而是不让它们被修改、新增或删除。需在 handler 中覆盖以下陷阱方法:
-
set:拦截所有赋值操作(包括新增属性),返回
false或抛出错误 -
deleteProperty:拦截
delete obj.prop,返回false -
defineProperty:拦截
Object.defineProperty和直接添加属性(如obj.newProp = 1),返回false - (可选)setPrototypeOf 和 preventExtensions:防止原型篡改或扩展对象
基础只读代理示例
下面是一个简洁可靠的只读代理实现:
function readonly(obj) {
return new Proxy(obj, {
set(target, key, value) {
throw new TypeError(`Cannot assign to read-only property '${key}'`);
},
deleteProperty(target, key) {
throw new TypeError(`Cannot delete property '${key}'`);
},
defineProperty(target, key, descriptor) {
throw new TypeError(`Cannot define property '${key}'`);
},
// 防止通过 Object.setPrototypeOf 修改原型
setPrototypeOf() {
throw new TypeError('Cannot set prototype of a readonly object');
},
// 防止后续扩展(配合 preventExtensions 使用更严格)
preventExtensions() {
return true;
}
});
}
const data = { name: 'Alice', age: 30 };
const roData = readonly(data);
roData.name = 'Bob'; // TypeError
delete roData.age; // TypeError
roData.city = 'Beijing'; // TypeError
深度只读:递归代理嵌套对象
上面的代理只保护顶层属性。若对象含嵌套对象,需递归包装——每次访问属性时,对返回值也做只读处理(在 get 拦截中判断并代理):
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
立即学习“Java免费学习笔记(深入)”;
function deepReadonly(obj) {
if (obj !== null && typeof obj === 'object') {
// 已代理过,避免重复代理
if (obj[Symbol.toStringTag] === 'ReadOnlyProxy') return obj;
return new Proxy(obj, {
get(target, key, receiver) {
const val = Reflect.get(target, key, receiver);
// 对对象类型(非 null)递归代理
if (val !== null && typeof val === 'object') {
return deepReadonly(val);
}
return val;
},
set() { throw new TypeError('Cannot modify readonly object'); },
deleteProperty() { throw new TypeError('Cannot delete property'); },
defineProperty() { throw new TypeError('Cannot define property'); },
setPrototypeOf() { throw new TypeError('Cannot set prototype'); },
preventExtensions() { return true; }
});
}
return obj;
}
这样 roData.profile.address.zip 也会受保护,任何层级的修改都会报错。
兼容性与注意事项
Proxy 是 ES2015+ 特性,不支持 IE;现代浏览器和 Node.js ≥ 6 均可用。实际使用注意:
- 代理对象与原对象不相等(
roData !== data),但内容一致 - 只读是运行时保护,不影响 TypeScript 类型检查(需额外类型标注)
- 若需冻结已有属性的可写性,可先用
Object.freeze(),再加Proxy补强拦截(如防止defineProperty绕过) - 避免在
get中无条件递归代理,防止循环引用导致栈溢出(可加缓存或 WeakMap 记录已代理对象)

















