Proxy 是对象的拦截代理层,创建新对象拦截对它的操作而非包装原对象;必须掌握 get/set/has/ownKeys 四个基础 trap,代理数组时需特别处理 length 和原型方法,且注意性能与 IE 兼容性问题。

Proxy 是什么:不是包装器,是对象的拦截代理层
Proxy 不是给对象加一层“壳”,而是创建一个新对象,所有对它的访问(读、写、枚举、构造等)都会被重定向到你定义的 handler 中处理。原始对象本身完全不受影响,Proxy 拦截的是对代理对象的操作行为,不是修改原对象的属性访问机制。
常见误解是把它当“响应式封装工具”直接用,结果发现 Object.defineProperty 那套逻辑不适用——因为 Proxy 拦截的是运行时行为,不是静态属性定义。
必须掌握的 4 个基础 trap:get/set/has/ownKeys
这四个 trap 覆盖了日常 90% 的拦截需求。漏掉 has 或 ownKeys,会导致 in 操作符、for...in、Object.keys() 等行为不符合预期,尤其在做数据校验或权限控制时容易出错。
-
get(target, prop, receiver):拦截属性读取。注意必须返回值,否则为undefined;若要保持原行为,需显式return Reflect.get(...arguments) -
set(target, prop, value, receiver):拦截赋值。必须返回布尔值(true表示成功),否则会抛TypeError -
has(target, prop):拦截prop in proxy。默认不触发,必须手动实现,否则始终返回true(因为 Proxy 默认透传) -
ownKeys(target):拦截Object.getOwnPropertyNames()和Reflect.ownKeys()。若目标对象有 Symbol 属性或不可枚举属性,这里必须显式返回,否则会被过滤掉
const target = { a: 1, b: 2 };
const handler = {
get(target, prop) {
console.log('get', prop);
return prop in target ? target[prop] : 'default';
},
set(target, prop, value) {
if (typeof value !== 'number') {
throw new TypeError('Only number allowed');
}
target[prop] = value;
return true;
},
has(target, prop) {
return prop !== 'secret' && prop in target;
},
ownKeys(target) {
return Object.keys(target);
}
};
const proxy = new Proxy(target, handler);
为什么不能直接代理数组?length 和索引更新会失效
Proxy 可以代理数组,但 push、pop、length 修改等操作不会自动触发 set,因为它们本质是调用原型方法,而数组的 length 是一个有 setter 的 accessor 属性——但 Proxy 默认不拦截原型链上的操作,除非你在 set 中额外判断 prop === 'length' 或拦截 defineProperty。
立即学习“Java免费学习笔记(深入)”;
更关键的是:proxy.push(1) 触发的是 proxy[proxy.length] = 1 + proxy.length++,后者会走 set,但前者若没实现 get 对 length 的响应,就可能读到旧值。
- 代理数组时,务必在
get中处理length的读取逻辑 - 若需拦截
push/splice等方法,得在handler中重写对应方法,或用applytrap 拦截函数调用 - 不要依赖
Array.isArray(proxy)判断类型——它返回true,但某些库(如 Vue 2 的响应式)内部靠__proto__或构造器识别,Proxy 会破坏该链
性能与兼容性:别在热路径上无脑套 Proxy
每次属性访问都多一层函数调用,V8 虽已优化,但在循环体、渲染函数、高频事件回调中滥用 Proxy,会明显拖慢执行速度。尤其当 handler 里有复杂逻辑(如深克隆、正则匹配、网络请求)时,问题立刻暴露。
兼容性方面:Proxy 不支持 IE,且 Node.js Object.defineProperty + 递归代理(仅限已知属性)。
- 生产环境建议只对明确需要拦截的顶层对象使用 Proxy,避免代理整个 store 或全局状态树
- 开发阶段可用
console.trace()在 trap 里打点,确认是否被意外高频触发 - Chrome DevTools 的 “Blackbox script” 功能可隐藏 Proxy 内部代码,避免调试时跳进 handler
set 却忘了同步更新 length,或者重写了 get 却没透传 Symbol.iterator,结果 [...proxy] 就崩了。











