news 2026/6/10 12:35:21

理解 Proxy 原理及如何拦截 Map、Set 等集合方法调用实现自定义拦截和日志——含示例代码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
理解 Proxy 原理及如何拦截 Map、Set 等集合方法调用实现自定义拦截和日志——含示例代码解析

先理解 Proxy 的核心思想

Proxy 就像一个“拦截器”,它可以“监听”一个对象的操作,比如:

  • 访问对象的属性(读取) → 触发get拦截器
  • 给对象的属性赋值(写入) → 触发set拦截器
  • 调用对象的方法 → 其实是先访问方法(触发get),再执行它

但集合类型(Map、Set 等)不直接用属性赋值来写入数据

  • Map 写入数据是调用它的set(key, value)方法
  • Set 写入数据是调用它的add(value)方法
  • 读取数据是调用 Map 的get(key)或 Set 的has(value)方法

所以,我们想拦截“写入”操作,就要拦截这些方法的调用。


Proxy 怎么拦截方法调用?

  • 当你访问proxyMap.set,会触发 Proxy 的get拦截器,告诉你访问了set方法。
  • 这时我们返回一个“包装函数”,这个函数内部可以插入自定义逻辑(比如打印日志),然后再调用原始的set方法。
  • 这样就实现了“拦截写入操作”。

具体示例:拦截 Map 的读取和写入

constmap=newMap();consthandler={get(target,prop,receiver){// 访问属性或方法时触发constorigMethod=target[prop];if(typeoforigMethod==='function'){// 如果访问的是方法,返回一个包装函数returnfunction(...args){if(prop==='set'){console.log(`写入操作:set(${args[0]},${args[1]})`);}elseif(prop==='get'){console.log(`读取操作:get(${args[0]})`);}// 调用原始方法returnorigMethod.apply(target,args);};}// 访问普通属性,直接返回returnReflect.get(target,prop,receiver);}};constproxyMap=newProxy(map,handler);proxyMap.set('name','CodeMoss');// 控制台输出:写入操作:set(name, CodeMoss)console.log(proxyMap.get('name'));// 控制台输出:读取操作:get(name)// 输出:CodeMoss

可以把它理解成:

  • 访问proxyMap.set→ Proxy 拦截,返回一个“带日志”的函数
  • 调用这个函数时,先打印日志,再调用真正的map.set

Set 也是类似的,只是写入方法叫add,读取方法叫has

constset=newSet();consthandler={get(target,prop,receiver){constorigMethod=target[prop];if(typeoforigMethod==='function'){returnfunction(...args){if(prop==='add'){console.log(`写入操作:add(${args[0]})`);}elseif(prop==='has'){console.log(`读取操作:has(${args[0]})`);}returnorigMethod.apply(target,args);};}returnReflect.get(target,prop,receiver);}};constproxySet=newProxy(set,handler);proxySet.add(123);// 控制台输出:写入操作:add(123)console.log(proxySet.has(123));// 控制台输出:读取操作:has(123)// 输出:true

WeakMap 和 WeakSet 也一样,只是它们的键或值必须是对象,且不能遍历

constweakMap=newWeakMap();consthandler={get(target,prop,receiver){constorigMethod=target[prop];if(typeoforigMethod==='function'){returnfunction(...args){if(prop==='set'){console.log('WeakMap 写入操作,键:',args[0],'值:',args[1]);}elseif(prop==='get'){console.log('WeakMap 读取操作,键:',args[0]);}returnorigMethod.apply(target,args);};}returnReflect.get(target,prop,receiver);}};constproxyWeakMap=newProxy(weakMap,handler);constobjKey={};proxyWeakMap.set(objKey,'secret');// 控制台输出:WeakMap 写入操作,键: {} 值: secretconsole.log(proxyWeakMap.get(objKey));// 控制台输出:WeakMap 读取操作,键: {}// 输出:secret

总结

  • Proxy 的get拦截器拦截的是“属性访问”,方法调用是先访问方法再执行。
  • 集合的写入和读取都是通过调用方法实现的,所以我们拦截方法访问,返回包装函数。
  • 包装函数里可以插入自定义逻辑(日志、权限等),然后调用原始方法完成操作。

把 const objKey = {}; 换成 map”,把 WeakMap 的键从一个普通对象{}换成一个 Map 对象。


先说明一点:

WeakMap 的键必须是对象,而 Map 本身是一个对象(它是一个构造函数实例),所以理论上是可以作为 WeakMap 的键的。


可以这样写:

constweakMap=newWeakMap();consthandler={get(target,prop,receiver){constorigMethod=target[prop];if(typeoforigMethod==='function'){returnfunction(...args){if(prop==='set'){console.log('WeakMap 写入操作,键:',args[0],'值:',args[1]);}elseif(prop==='get'){console.log('WeakMap 读取操作,键:',args[0]);}returnorigMethod.apply(target,args);};}returnReflect.get(target,prop,receiver);}};constproxyWeakMap=newProxy(weakMap,handler);// 这里用 Map 作为键constmapKey=newMap();proxyWeakMap.set(mapKey,'secret');// 控制台输出:WeakMap 写入操作,键: Map {} 值: secretconsole.log(proxyWeakMap.get(mapKey));// 控制台输出:WeakMap 读取操作,键: Map {}// 输出:secret

解释:

  • mapKey是一个 Map 实例,属于对象类型,可以作为 WeakMap 的键。
  • WeakMap 允许任何对象作为键,包括普通对象、数组、函数、甚至 Map、Set 等实例。
  • 用 Map 作为键,完全没问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/7 12:51:50

终极指南:快速掌握Scarab空洞骑士模组管理神器

终极指南:快速掌握Scarab空洞骑士模组管理神器 【免费下载链接】Scarab An installer for Hollow Knight mods written in Avalonia. 项目地址: https://gitcode.com/gh_mirrors/sc/Scarab 想要轻松管理几十个空洞骑士模组却不知从何下手?Scarab作…

作者头像 李华
网站建设 2026/6/9 0:31:50

百度网盘高速下载工具使用手册:告别蜗牛速度的秘诀

还在为百度网盘那令人抓狂的下载速度而烦恼吗?想象一下,原本需要几个小时下载的文件,现在只需几分钟就能完成!今天我要分享的这款神器,正是为解决这一痛点而生——百度网盘高速下载工具,让你的下载体验瞬间…

作者头像 李华