用 Proxy 实现对象属性只读需拦截 set、deleteProperty 和 defineProperty 三个 trap,其中 set 拦截最关键,返回 false 或抛 TypeError 可阻止赋值;deleteProperty 和 defineProperty 同样需返回 false 或抛错以禁止删除和重定义属性;get 拦截可选,用于对引用类型调用 Object.freeze 实现浅只读。

用 Proxy 实现对象属性只读,核心是拦截 set、deleteProperty 和 defineProperty 这三个 trap,同时可选地拦截 get 来统一返回不可变值(如深冻结后的副本)。
拦截赋值操作(set)
这是最关键的一步。只要在 set 中直接返回 false 或抛出错误,就能阻止任何属性修改:
- 返回
false:静默失败(严格模式下会报TypeError) - 显式
throw new TypeError(...):无论是否严格模式都报错,行为更可控 - 注意:必须返回布尔值,否则会触发默认行为(即允许赋值)
拦截删除和重定义(deleteProperty & defineProperty)
只禁 set 不够——用户仍可能 delete obj.x 或 Object.defineProperty(obj, 'x', {...}) 来破坏只读性:
-
deleteProperty返回false或抛错,阻止属性删除 -
defineProperty同样返回false或抛错,阻止新增/改写属性描述符 - 若目标对象本身是
frozen,这些操作本就会失败,但代理层拦截更明确、更早
可选:拦截读取(get)实现“深只读”语义
如果希望返回的属性值也只读(比如嵌套对象),可在 get 中对返回值做处理:
立即学习“Java免费学习笔记(深入)”;
- 对引用类型(对象、数组)调用
Object.freeze()再返回(浅冻结) - 如需真正深只读,可用递归
deepFreeze工具函数(注意循环引用) - 简单场景下,也可只对已冻结的对象返回原值,避免重复冻结开销
完整示例代码
以下是一个实用的只读代理工厂:
function readonly(target) {
return new Proxy(target, {
set() {
throw new TypeError('Cannot modify read-only object');
},
deleteProperty() {
throw new TypeError('Cannot delete property of read-only object');
},
defineProperty() {
throw new TypeError('Cannot define property on read-only object');
},
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
// 可选:对对象类型自动冻结(浅)
if (value !== null && typeof value === 'object') {
return Object.freeze(value);
}
return value;
}
});
}
// 使用
const obj = { a: 1, b: { c: 2 } };
const ro = readonly(obj);
ro.a = 99; // TypeError
ro.b.c = 99; // 报错(因 b 被 freeze)


















