proxy无法真正使对象不可变,但可通过set/deleteproperty/setprototypeof等拦截器阻止赋值、删除和原型修改,并结合object.freeze、递归代理及数组方法重写实现逻辑上不可变。

JavaScript 中 Proxy 本身不能直接让对象“真正不可变”,但可以通过拦截关键操作(如赋值、删除、原型修改等),在运行时抛出错误或返回新对象,从而模拟不可变语义,实现“逻辑上不可变”的数据结构保护。
拦截赋值与属性新增(set)
通过 set 拦截器阻止任何属性写入操作,包括新增和修改:
function makeImmutable(obj) {
return new Proxy(obj, {
set(target, key, value) {
throw new TypeError(`Cannot assign to immutable object: ${key}`);
}
});
}
const data = makeImmutable({ a: 1 });
data.a = 2; // ❌ 抛出 TypeError
data.b = 3; // ❌ 同样报错(新增也拦截)
注意:该方式对嵌套对象无效——若 data.nested 是普通对象,对其赋值仍可成功。需递归代理或配合 get 动态包装子属性。
拦截属性删除与原型变更(deleteProperty / setPrototypeOf)
不可变数据不应允许删属性或换原型,这两项必须显式拦截:
macOS 微信消息自动化工具。通过 GUI 自动化实现:发送消息给指定联系人、读取聊天内容、监控新消息。适用于需要自动化微信操作的场景,如定时发送、批量回复、消息备份等。依赖 peekaboo 进行屏幕截图和 UI 交互。仅支持 macOS。开源地址:https://github.com/chairmanmia...
-
deleteProperty:拦截delete obj.key -
setPrototypeOf:拦截Object.setPrototypeOf(obj, ...)和obj.__proto__ = ...
const handler = {
set() { throw new TypeError('Immutable: set denied'); },
deleteProperty() { throw new TypeError('Immutable: delete denied'); },
setPrototypeOf() { throw new TypeError('Immutable: setPrototypeOf denied'); }
};
拦截数组方法(push、pop、splice 等)
原生数组方法会直接修改自身,Proxy 的 set 拦截不到这些调用。需重写数组方法,或用 apply 拦截函数调用:
- 对数组实例,可代理其方法(如
push)返回新数组(类似 Immutable.js 思路) - 更实用的做法:用
get拦截器对数组方法返回“惰性包装”或抛错
function immutableArray(arr) {
return new Proxy(arr, {
get(target, prop) {
if (typeof target[prop] === 'function') {
return function(...args) {
throw new TypeError(`Array method "${prop}" mutates data — use functional alternatives like [...arr, x]`);
};
}
return target[prop];
}
});
}
配合 Object.freeze 与深度代理提升安全性
Proxy 单独使用无法防止 Object.defineProperty 或 Object.assign 等底层操作;建议组合使用:
- 先
Object.freeze(obj)阻止扩展、写入、配置变更(基础防护) - 再用 Proxy 拦截动态行为(如 in 操作符、for...in、Reflect.ownKeys)
- 对嵌套结构,可在
get中自动代理子对象/数组(懒代理)
例如:get 返回子属性时,若值为对象且未代理过,则递归 wrap 为 immutable proxy。
不复杂但容易忽略:真正的不可变需要防御所有可能的修改入口,Proxy 是最灵活的拦截层,但需配合冻结、方法重写和递归策略才能覆盖常见场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










