当前位置: 首页 > java >正文

JS魔法中介:Proxy和Reflect为何形影不离?


🔥 以龙息淬炼代码,在时光灰烬中重铸技术星河 !

欢迎来到 晷龙烬的博客小窝✨!
这里记录技术学习点滴,分享实用技巧,偶尔聊聊奇思妙想~

原创内容✍️,转载请注明出处~感谢支持❤️!请尊重原创📩!
欢迎在评论区交流🌟!

引言

你有没有遇到过这种场景:想监控对象属性的读写、想给对象加点“隐藏技能”,又不想直接修改原对象?就像给房子装个智能管家,既能控制访客,又不破坏房子本身。
ES6 带来的 ProxyReflect 就是干这个的!但很多人 ES6 用得溜,却忽略了这对“黄金搭档”。今天咱们就来说一下他们俩个!

一、Proxy:你的专属“对象中介”

想象你有个对象 user,它像一栋房子:

const user = { name: "晷龙烬", age: 18 };  

现在你想监控谁进了这房子(读属性)、谁改了装修(写属性)。直接改 user 代码?太粗暴!
Proxy 登场——它给对象套个“中介壳”,所有操作都得通过它。Proxy 能拦截13种操作(称为 陷阱/trap),包括属性读写、删除、函数调用等:

const userProxy = new Proxy(user, {  get(target, key) {  console.log(`有人查看了你的${key}`);  return target[key]; // 返回原属性值  },  set(target, key, value) {  console.log(`有人把${key}改成了${value}`);  target[key] = value;  return true; // 告诉JS设置成功  },  has(target, key) { // 新增:拦截in操作符  console.log(`检查${key}是否存在?`);  return key in target;  },  deleteProperty(target, key) { // 新增:拦截删除  console.log(`试图删除${key}`);  delete target[key];  return true;  }  
});  
userProxy.age; // 输出:有人查看了你的age!  
"age" in userProxy; // 输出:检查age是否存在?  
delete userProxy.name; // 输出:试图删除name!  

关键点

  • Proxy 是个“壳”,不改变原对象,但能代理数组(如监听push操作),而 Object.de fineProperty 无法做到。

  • 常用陷阱包括 getsethas(属性存在检查)、deleteProperty(删除属性)等,共13种。

  • 适用场景

    :数据校验、日志监控、自动填充属性。例如,代理数组实现长度监控:

    const list = [1, 2, 3];  
    const listProxy = new Proxy(list, {  set(target, key, value) {  if (key === "length") console.log("数组长度变了!");  return Reflect.set(target, key, value);  }  
    });  
    listProxy.push(4); // 触发length变化  
    

二、Reflect:Proxy的“最佳拍档”

上面代码用 target[key] 直接操作原对象,但这样有问题:如果原对象有getter/setter或继承属性,可能出错!这时就需要 Reflect——它提供13个静态方法,与Proxy陷阱一一对应,确保操作安全:

const userProxy = new Proxy(user, {  get(target, key, receiver) {  console.log(`拦截查看: ${key}`);  return Reflect.get(target, key, receiver); // 使用Reflect解决this指向  },  set(target, key, value, receiver) {  console.log(`拦截修改: ${key}=>${value}`);  return Reflect.set(target, key, value, receiver); // 返回布尔值,便于错误处理  }  
});  

为什么必须用 Reflect?

  1. 行为统一Reflect 方法与 Proxy 陷阱完全对应(如 Reflect.ge t 对应 get 陷阱),代码更一致。
  2. 解决 this 陷阱:Proxy 代理后,对象方法内的 this 默认指向代理对象而非原对象,Reflect.ge treceiver 参数可修正此问题。
  3. 错误处理优化Reflect.set 返回布尔值(成功/失败),而直接赋值可能抛出异常,便于控制流。
  4. 减少“魔法” :ES6 将 delete obj.x 等操作标准化为 Reflect.de leteProperty(),提升可读性。

💡 核心原则:Proxy 负责拦截,Reflect 负责执行

三、高级应用🌰:属性校验与隐藏

场景1:自动必填校验

假设要做表单,要求 nameemail 必填。用 Proxy + Reflect 实现:

const form = {};  
const formProxy = new Proxy(form, {  set(target, key, value) {  if (key === 'name' || key === 'email') {  if (!value) throw new Error(`${key}不能为空!`);  }  return Reflect.set(target, key, value); // 安全设置,返回布尔值  }  
});  
formProxy.name = ""; // 报错:name不能为空!  
formProxy.email = "xx@csdn.net"; // 成功!  

优势

  • 无侵入form 保持普通对象。
  • 可扩展:加规则只需修改Proxy配置。
场景2:属性隐藏(新增)

has 陷阱隐藏敏感属性,实现数据封装:

const account = { id: "123", password: "secret" };  
const safeAccount = new Proxy(account, {  has(target, key) {  if (key === "password") return false; // 隐藏password  return key in target;  },  get(target, key) {  if (key === "password") throw new Error("无权访问!");  return Reflect.get(target, key);  }  
});  
console.log("password" in safeAccount); // false  
console.log(safeAccount.password); // 报错:无权访问!  

四、注意事项与局限性

Proxy 虽强大,但有坑:

  • this 指向问题:代理后,对象方法内的 this 可能指向代理而非原对象,需用 Reflectreceiver 参数纠正。
  • 无法代理内部属性:如 DategetDate() 方法,Date.prototype.getDate 方法需访问内部属性 [[NumberData]],但 Proxy 代理后,this 指向 proxy 而非原始 Date 实例,引擎无法找到。因为内部属性 [[NumberData]] 的存储位置独立于对象公开属性,Proxy 的拦截机制无法覆盖其访问路径。
  • 性能考量:频繁拦截可能影响性能,适合高阶场景(如框架开发)。

结语

Proxy + Reflect 就像对象的“智能控制层”:

  • Proxy 是门卫,精细控制13种操作(读、写、删、查等);

  • Reflect 是万能工具包,确保操作安全、标准化。

它们在前端生态中至关重要:常见的Vue 就是用其实现响应式。下次遇到“监控对象”需求,别写一堆 getXXX() 了——掌握这对组合,代码灵活度飙升!✨


—— 完 ——

✨ 至此结束 ✨

💡 点赞关注,解锁更多技术干货!

我是 晷龙烬
期待与你的下次相遇~
可爱的小龙猫

http://www.xdnf.cn/news/20318.html

相关文章:

  • 【1】MOS管的结构及其工作原理
  • Linux系统: docker安装RagFlow教程
  • 【工具变量】上市公司企业海外业务收入数据集(2003-2024年)
  • C++ map和set
  • 2025年经济学专业女生必考证书指南:打造差异化竞争力
  • Netty从0到1系列之JDK零拷贝技术
  • Spring DI详解--依赖注入的三种方式及优缺点分析
  • Windows 权限提升(一)
  • ES模块(ESM)、CommonJS(CJS)和UMD三种格式
  • Java全栈学习笔记30
  • RX 9 Audio Editor 音频编辑器安装教程(v9.3.0 Windows版)
  • if __name__=‘__main__‘的用处
  • 推荐收藏!5款低代码工具,告别复杂开发!
  • 8051单片机-蜂鸣器
  • 数据库索引结构 B 树、B + 树与哈希索引在不同数据查询场景下的适用性分析
  • vue-amap组件呈现的效果图如何截图
  • 米尔RK3576部署端侧多模态多轮对话,6TOPS算力驱动30亿参数LLM
  • MySQL数据库基础(DCL,DDL,DML)详解
  • Axure笔记
  • 【VoNR】VoNR是5G语音,VoLTE是4G语音,他们是同一个IMS,只是使用了新的访问方式?
  • 传统神经网络实现-----手写数字识别(MNIST)项目
  • 状压 dp --- 棋盘覆盖问题
  • 使用smb协议同步远程文件失败
  • javaweb(【概述和安装】【tomeat的使用】【servlet入门】).
  • SQL工具30年演进史:从Oracle到Navicat、DBeaver,再到Web原生SQLynx
  • 【开题答辩全过程】以 智能商品数据分析系统为例,包含答辩的问题和答案
  • 商密保护密码:非公知性鉴定的攻防之道
  • 介电常数何解?
  • 苍穹外卖 day03
  • 数字时代的 “安全刚需”:为什么销售管理企业都在做手机号码脱敏