优雅而高效的JavaScript——Proxy 和 Reflect

在这里插入图片描述
🤔博主:小猫娃来啦
文章核心:优雅而高效的JavaScript——Proxy 和 Reflect

文章目录

  • Proxy 和 Reflect是什么
  • Proxy
    • 创建 Proxy 对象
    • 拦截器方法
    • 拦截器示例:属性拦截
    • 拦截器示例:方法拦截
  • Reflect
    • Reflect 的静态方法
    • Reflect 示例:拦截对象操作
    • Reflect 示例:操作原型链
  • Proxy 和 Reflect 的应用
    • 对象代理
    • 元编程
  • 总结

Proxy 和 Reflect是什么

Proxy 和 Reflect 是两个强大的功能,它们可以为我们提供了更大的灵活性和控制力,以创建自定义行为的对象代理和实现元编程的功能。Proxy 是用于创建对象代理的特性,它可以拦截并操作对象的底层操作。Reflect 是一个内置对象,提供了一组静态方法,用于执行与 Proxy 相关的默认行为。


Proxy

创建 Proxy 对象

要创建一个 Proxy 对象,我们使用 Proxy 构造函数,并传入两个参数:目标对象和一个处理程序对象。目标对象是被代理的对象,处理程序对象定义了在拦截器方法中实现的自定义行为。

const target = {}; // 目标对象
const handler = {}; // 处理程序对象
const proxy = new Proxy(target, handler);

拦截器方法

在处理程序对象中,我们可以定义一组拦截器方法。每个拦截器方法对应一个底层操作,当执行底层操作时,拦截器方法将会被触发。

以下是一些常见的拦截器方法:

  • get(target, property, receiver): 拦截对象的属性读取操作。
  • set(target, property, value, receiver): 拦截对象的属性写入操作。
  • apply(target, thisArg, argumentsList): 拦截函数的调用操作。
  • construct(target, argumentsList, newTarget): 拦截类的实例化操作。

拦截器示例:属性拦截

让我们通过一个示例来理解属性拦截。

const target = {name: 'John',age: 30
};const handler = {get(target, property, receiver) {console.log(`读取属性:${property}`);return target[property];},set(target, property, value, receiver) {console.log(`设置属性:${property} = ${value}`);target[property] = value;return true;}
};const proxy = new Proxy(target, handler);console.log(proxy.name); // 读取属性:name,输出:Johnproxy.age = 35; // 设置属性:age = 35
console.log(proxy.age); // 读取属性:age,输出:35

在上面的代码中,我们创建了一个目标对象 target,并定义了一个处理程序对象 handler,其中的 get 和 set 方法分别用于拦截属性的读取和写入操作。通过创建 Proxy 对象 proxy,我们可以访问目标对象的属性,并在每个操作上触发拦截器方法。

拦截器示例:方法拦截

除了属性拦截,我们还可以使用拦截器方法拦截函数的调用操作。让我们看一个例子:

const target = {sum(x, y) {return x + y;}
};const handler = {apply(target, thisArg, argumentsList) {console.log('调用 sum 方法');return target.sum(...argumentsList);}
};const proxy = new Proxy(target, handler);console.log(proxy.sum(2, 3)); // 调用 sum 方法,输出:5

在上面的代码中,我们定义了一个目标对象 target,其中的 sum 方法接受两个参数并返回它们的和。使用 apply 拦截器方法,我们可以在每次调用 sum 方法时触发一些自定义行为。


Reflect

Reflect 的静态方法

Reflect 是一个内置对象,提供了一组静态方法,用于执行与 Proxy 相关的默认行为。这些方法与拦截器方法相对应,它们提供了一种简单的方式来调用默认行为,而不是完全重写拦截器方法。

以下是一些 Reflect 的静态方法:

  • Reflect.get(target, property, receiver): 访问指定对象的属性。
  • Reflect.set(target, property, value, receiver): 设置指定对象的属性。
  • Reflect.apply(target, thisArg, argumentsList): 调用指定的函数。
  • Reflect.construct(target, argumentsList, newTarget): 创建指定类的实例。

Reflect 示例:拦截对象操作

让我们通过一个示例来理解如何使用 Reflect 的方法来拦截对象操作。

const target = {name: 'John',age: 30
};const handler = {get(target, property, receiver) {console.log(`读取属性:${property}`);return Reflect.get(target, property, receiver);},set(target, property, value, receiver) {console.log(`设置属性:${property} = ${value}`);return Reflect.set(target, property, value, receiver);}
};const proxy = new Proxy(target, handler);console.log(proxy.name); // 读取属性:name,输出:Johnproxy.age = 35; // 设置属性:age = 35
console.log(proxy.age); // 读取属性:age,输出:35

在上面的代码中,我们使用 Reflect 的 get 和 set 方法在拦截器方法中调用默认行为。通过使用 Reflect,我们可以避免完全重写拦截器方法,而只关注需要自定义的行为。

Reflect 示例:操作原型链

除了拦截对象属性的读写操作,Reflect 还提供了一些方法来操作原型链。让我们看一个例子:

class Person {constructor(name) {this.name = name;}
}const handler = {has(target, property) {console.log(`检查属性:${property}`);return Reflect.has(target, property);},get(target, property, receiver) {console.log(`读取属性:${property}`);return Reflect.get(target, property, receiver);}
};const proxy = new Proxy(Person, handler);console.log('name' in proxy); // 检查属性:name,输出:trueconst john = new proxy('John');
console.log(john.name); // 读取属性:name,输出:John

在上面的代码中,我们创建了一个 Person 类,并使用 has 和 get 方法拦截了原型链操作。通过创建 Proxy 对象 proxy,并将 Person 类传递给它,我们可以在每次操作原型链时触发自定义行为。


Proxy 和 Reflect 的应用

对象代理

通过使用 Proxy 和 Reflect,我们可以创建对象代理来拦截和加工对象的底层操作。

例如,我们可以使用 Proxy 来创建一个简单的缓存代理:

const cache = new Map();const handler = {get(target, property, receiver) {if (cache.has(property)) {console.log(`从缓存中读取属性:${property}`);return cache.get(property);}const value = Reflect.get(target, property, receiver);cache.set(property, value);console.log(`将属性缓存:${property}`);return value;},set(target, property, value, receiver) {console.log(`设置属性:${property} = ${value}`);cache.set(property, value);return Reflect.set(target, property, value, receiver);}
};const obj = new Proxy({}, handler);obj.name = 'John'; // 设置属性:name = John
console.log(obj.name); // 从缓存中读取属性:name,输出:Johnobj.name = 'Jane'; // 设置属性:name = Jane
console.log(obj.name); // 从缓存中读取属性:name,输出:Jane

在上面的代码中,我们创建了一个对象代理,它使用一个 Map 缓存对象的属性。在拦截器方法中,我们首先检查缓存中是否存在属性值,如果有,我们直接从缓存中读取。否则,我们使用 Reflect.get 方法获取属性值,并将其存储到缓存中。

元编程

元编程是指编写能够操作自身行为的代码。

通过使用 Proxy 和 Reflect,我们可以实现一些元编程的功能,例如动态属性访问、属性校验、方法调用等。

让我们看一个示例,使用 Proxy 实现动态属性访问:

const person = {name: 'John',age: 30
};const handler = {get(target, property) {if (!(property in target)) {throw new Error(`属性不存在:${property}`);}return Reflect.get(target, property);}
};const proxy = new Proxy(person, handler);console.log(proxy.name); // John
console.log(proxy.age); // 30
console.log(proxy.city); // 抛出错误:属性不存在:city

在上面的代码中,我们使用 Proxy 实现动态属性访问。在处理程序对象的 get 方法中,我们首先检查属性是否存在于目标对象中。如果不存在,我们抛出一个错误。否则,我们使用 Reflect.get 方法获取属性值。


总结

Proxy 和 Reflect 是 JavaScript 中强大的特性,它们为我们提供了更大的灵活性和控制力来创建自定义行为的对象代理和实现元编程的功能。在本文中,我们学习了 Proxy 和 Reflect 的基本概念,介绍了它们的使用方法和示例。我们还探讨了 Proxy 和 Reflect 的应用领域,包括对象代理和元编程。希望通过本文的学习,你对 Proxy 和 Reflect 的概念和用法有了更深入的理解。

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/108064.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ESP32单片机环境搭建(VScode + PlatformIO IDE)

一、环境搭建(VScode PlatformIO IDE) 1、官网下载VScode; 2、安装最新的插件(C/C、PlatformIO IDE、python、Chinese); 3、在PlatformIO IDE中新建工程:Platforms——Projects——Create New Project——…

软考 系统架构设计师系列知识点之基于架构的软件开发方法ABSD(6)

接前一篇文章: 所属章节: 第7章. 系统架构设计基础知识 第5节. 特定领域软件体系结构 相关试题 1. 基于架构的软件设计(ABSD)强调由商业、质量和功能需求的组合驱动软件架构设计。ABSD方法有三个基础:功能分解、&…

P2251 质量检测

题目&#xff1a; P2251 质量检测 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 代码&#xff1a; #include<iostream> #include<cstdio> #include<deque> #include<vector> typedef long long ll; const ll N 1e7; using namespace std;int main…

vscode - 环境准备 - 修改缓存路径

说明 在window上使用vscode&#xff0c;其默认的缓存&#xff08;包含代码缓存&#xff0c;插件等&#xff09;存储路径是在c盘&#xff0c;即使将vscode安装在其它磁盘。频繁使用vscode&#xff0c;vscode会占用C盘大量磁盘空间&#xff0c;C盘空间不充裕&#xff0c;会被vsc…

babili-webpack-plugin编译后的代码v+‘‘===1,会被压缩成字符串v1

同问题&#xff1a;https://github.com/babel/minify/issues/1021 然后翻了下源码 使用了 babel-preset-babili, 继续往下找发现它又使用了babel插件:babel-plugin-minify-constant-folding 这个插件主要功能是将javascript代码中无用的常量、表达式、函数等内容进行折叠并优…

SPI 接口 CAN协议控制器 MCP2515/DP2515国产替代芯片DPC15

can控制器是CAN局域网控制器的简称&#xff0c;为解决现代汽车中众多测量控制部件之间的数据交换而开发的一种串行数据通信总线。 CAN 可提供高达1Mbit/s的数据传输速率&#xff0c;这使实时控制变得非常容易。另外&#xff0c;硬件的错误检定特性也增强了CAN的抗电磁干扰能力…

[MQ]Win平台RocketMQ安装启动

1、下载 官网下载地址&#xff1a;https://rocketmq.apache.org/zh/download 2、解压ZIP包 解压rocketmq-all-x.x.x-bin-release.zip到目录。 比如我解压到了E:\Env\MQ_rocket\rocketmq-all-5.1.4-bin-release 3、配置环境变量 ROCKETMQ_HOME 4、RocketMQ JVM内存配置 这个需要…

架构案例2017(五十二)

第5题 阅读以下关于Web系统架构设计的叙述&#xff0c;在答题纸上回答问题1至问题3.【说明】某电子商务企业因发展良好&#xff0c;客户量逐步增大&#xff0c;企业业务不断扩充&#xff0c;导致其原有的B2C商品交易平台己不能满足现有业务需求。因此&#xff0c;该企业委托某…

上网冲浪发现多处XSS

突然的发现 今天上网冲浪&#xff0c;突然想起来有一种神器&#xff0c;叫废话生成器&#xff0c;之前是在哪里下了个软件玩了一下&#xff0c;然后就给删除了&#xff0c;因为我觉得这个软件不过就是调用了一个web接口实现的&#xff0c;一个网页能解决的事还要我下一个软件。…

麒麟系统 部署 kubernetes v1.18.5

1. 背景 手工部署 Kubernetes 二进制集群相对于使用自动化工具或发行版进行部署有一些优势&#xff1a; 定制性&#xff1a;手工部署允许您对 Kubernetes 集群的各个组件进行定制。您可以选择特定的版本、配置选项和插件&#xff0c;以满足您的需求。这种灵活性使您能够根据具…

系列八、Redis的事务

一、是什么 可以一次执行多个命令&#xff0c;本质是一组命令的集合。一个事务中的所有命令都会序列化&#xff0c;按顺序地串行化执行而不会被其他命令插入&#xff0c;不允许加塞。 二、能干嘛 一个队列中&#xff0c;一次性、顺序性、排他性的执行一些列命令。 三、怎么玩…

【蓝桥】奇怪的线段

一、题目 1、题目描述 在一维数轴上&#xff0c;小蓝画了 n n n 个闭区间线段&#xff0c;小桥会多次询问你&#xff0c;每次给定两个点 a , b a, b a,b&#xff0c;问有多少个区间包含 a a a 点&#xff0c;但是不包含 b b b 点。 输入格式 第一行输入两个整数 n , q…

【ARM Coresight Debug 系列 16 -- Linux 断点 BRK 中断使用详细介绍】

文章目录 1.1 ARM BRK 指令1.2 BRK 立即数宏定义介绍1.3 断点异常处理流程1.3.1 el1_sync_handler1.3.2 el1_dbg 跟踪 1.4 debug 异常处理函数注册1.4.1 brk 处理函数的注册 1.1 ARM BRK 指令 ARMv8 架构的 BRK 指令是用于生成一个软件断点的。当处理器执行到 BRK 指令时&…

【6-1 CString字符串类】武汉理工大学

6-1 CString字符串类 分数 20 作者 谢颂华 单位 武汉理工大学 定义一个字符串类CString&#xff0c;使其至少包含内容(contents)和长度(length)两个数据成员&#xff0c;要求定义以下成员函数&#xff1a;构造函数、显示字符串函数display()、求字符串长度函数getlength()和字符…

【2023年11月第四版教材】专题3 - 10大管理49过程输入、输出、工具和技术总结

专题3 - 10大管理49过程输入、输出、工具和技术总结 1 49个过程开展频次总结1.1 开展一次或仅在预定义时点开展的项目管理过程1.2 需要定期开展的过程1.3 需要持续开展的过程2 部分重要规律总结2.1 规划过程组的总体工作流程2.2 监控过程组的总体工作流程2.3 工作绩效数据、信息…

L1-039 古风排版 C++解法

题目再现 中国的古人写文字&#xff0c;是从右向左竖向排版的。本题就请你编写程序&#xff0c;把一段文字按古风排版。 输入格式&#xff1a; 输入在第一行给出一个正整数N&#xff08;<100&#xff09;&#xff0c;是每一列的字符数。第二行给出一个长度不超过1000的非…

4.2 抽象类

1. 抽象类概念 定义一个类时&#xff0c;常常需要定义一些成员方法用于描述类的行为特征&#xff0c;但有时这些方法的实现方式是无法确定的。例如&#xff0c;Animal类中的shout()方法用于描述动物的叫声&#xff0c;但是不同的动物&#xff0c;叫声也不相同&#xff0c;因此…

JMM 的设计意图

JMM 的设计意图 让我们来看JMM的设计意图。从JMM设计者的角度&#xff0c;在设计JMM时&#xff0c;需要考虑两个关键因素。 程序员角度编译器和处理器角度程序员对内存模型的使用。程序员希望内存模型易于理解、易于编程。程序员希望基于一个强内存模型来编写代码。编译器和…

从代码入手理解卡尔曼滤波器的原理之引入状态转换模型(四)

前面讲到没有外部控制输入,也没有其他动态模型来描述温度如何随时间变化。因此,状态x的预测值就是上一个状态的值。 默认在没有新的观测数据时,室温是恒定不变的。但在实际情况中,室温可能会随时间上升或下降,比如因为暖气的开启或窗户的打开。我们可以通过引入一个状态转…

倒计时4天|超硬核!第四届CID大会完整议程揭晓

CID大会官网&#xff1a; http://chinacid.org/ 原文链接&#xff1a;报名继续 | 超硬核&#xff01;第四届CID大会完整议程揭晓