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

news2024/12/30 2:23:01

在这里插入图片描述
🤔博主:小猫娃来啦
文章核心:优雅而高效的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,输出:John

proxy.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,输出:John

proxy.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,输出:true

const 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,输出:John

obj.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.coloradmin.cn/o/1100590.html

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

相关文章

ORACLE内存结构

内存体系结构 数据库由磁盘文件构成,当数据库启动时,相关实例将被启动,而实例由内存结构和进程组成。数据库及其运行的程序存放在分配给内存的不同结构当中。我们只讨论单实例的内存体系结构。内存跟磁盘空间分配一样,一般情况下…

Golang 面向对象编程 多态

基本介绍 变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的(接口能够体现多态的特征)。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。 在前面的Usb接口案例,u…

金融行业的CFA、CPA和FRM你了解吗?中国人民大学与加拿大女王大学金融硕士需要吗?

CFA、CPA和FRM是金融行业中非常重要的专业资格认证,它们分别代表着不同领域的专业知识和技能。在当下越来越多的高校在把关金融硕士的申请过程中,拥有CFA、CPA和FRM都属于强大的加分项,中国人民大学与加拿大女王大学金融硕士也不例外&#xf…

软件工程与计算总结(十六)详细设计的设计模式

一.设计模式基础 某种意义上来说,设计模式就是设计经验的总结~ 设计模式不是简单的经验总结,更不是无中生有,它是经过实践反复检验、能解决关键技术难题、有广泛应用前景和能够显著提高软件质量的有效的经验总结。 每个模式都不是独立的&a…

Word保存后内容丢失?4个方法,有效找回数据!

“我利用Word文档写了一份重要的工作报告。明明已经保存了,但是里面的内容却莫名其妙丢失了。这可怎么办呢?有什么方法可以找回丢失的Word内容吗?” Word作为一个常用的办公工具,为我们的工作提供了很多的便利。但在使用Word时&am…

uniapp+vue3+ts开发微信小程序+抖音小程序等跨平台应用入门,环境搭建

使用之前需要安装hbuilder和微信开发者工具,hbuilder是uniapp的开发工具,微信开发者工具是微信官方的小程序开发平台: hbuilder下载地址:HBuilderX-高效极客技巧 微信开发者工具:微信开发者工具下载地址与更新日志 |…

模块电源(四):可调DC-DC

一、DC-DC典型应用 以DC-DC转换器SCT2432数据手册为例,典型应用电路如下图所示: 其中,输出电压为: , DC-DC转换器中, 反馈电压是指反馈回路中的信号电压,用于控制输出电压与设定电压之间的误差&…

LeetCode 137. 只出现一次的数字 II【哈希表;位运算;数字逻辑;DFA】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…

【Linux基础】Linux 发展史

转载于 https://blog.csdn.net/weixin_45031801/article/details/133551510 文章目录 1、前言2、Linux 发展史2.1 UNIX 发展史2.1.1 C 语言对 UNIX 的影响 2.2 Linux 的诞生2.2.1 概述2.2.2 开源的优势2.3.3 系统结构内核层Shell层应用层 3、Linux 应用领域3.1 服务器领域3.2 …

Python算法练习 10.16

leetcode 437 路径总和III 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的&#xff…

SpringBoot集成Dubbo

1. 构建SpringBoot环境 1.1 创建一个duubo-spring-boot-demo项目 duubo-spring-boot-demo:是父工程,方便依赖管理duubo-spring-boot-demo-consumer:是服务消费方,继承父工程duubo-spring-boot-demo-provider:是服务提…

数据结构 2 第二章 线性结构 代码实现

头文件 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdlib.h>//malloc函数头文件 #include <time.h> #include <string.h> #include <limits.h> #include <ctype.h> #include <math.h> # include <stdio.h> 一、单链表 1.单链…

Segment Anything(论文解析)

Segment Anything 摘要1.介绍2 SAM任务SAM模型 摘要 我们介绍了“Segment Anything” (SA) 项目&#xff1a;这是一个新的任务、模型和数据集对于图像分割。使用我们高效的模型进行数据收集&#xff0c;我们构建了迄今为止最大的分割数据集&#xff08;远远超过其他数据集&…

C语言进行实验:通过程序实现线算图取值【支持VC++ 6.0编辑器环境运行】

背景&#xff1a; 一、实验目的和要求 1、能描述数据基本类型及其常量的表示方法&#xff1b; 2、会对变量进行定义及初始化&#xff1b; 3、能使用运算符与表达式对变量赋值&#xff1b; 4、会描述C语句的概念及种类、C语言常用的输入/出方式&#xff1b; 5、会设计顺序…

typora主题切换与推荐主题

在这篇博文中&#xff0c;我将向你展示如何给typora更换主题&#xff0c;并推荐几款出色的主题。通过这些主题的使用&#xff0c;你可以为你的typora编辑器增添一抹别样的风采&#xff0c;让你的写作体验更加美好、舒适。 typora替换主题的步骤非常简单&#xff0c;只需按照以…

科技资讯|苹果Vision Pro可通过手势ID检测不同用户

近日&#xff0c;美国专利局公布了苹果公司的一项专利申请&#xff0c;该专利申请涉及基于手部特征验证用户身份的技术。苹果指出&#xff0c;可能是多个家庭成员都想使用 Apple Vision Pro&#xff0c;系统必须识别不同的手势以控制 visionOS。在另一个示例中&#xff0c;苹果…

无频闪护眼灯哪个好?五款无频闪护眼台灯推荐

青少年的近视率持续升高&#xff0c;保护眼睛非常重要。台灯是用眼环境的必备品&#xff0c;而市面上款式多样不知如何购买。这期就来聊聊护眼台灯的选购问题&#xff01; 都说成人的世界不容易&#xff0c;社交网络上时常有人吐槽996工作制&#xff0c;但要知道的是现在的学生…

(vue)el-descriptions 描述列表无效

(vue)el-descriptions 描述列表无效 原因&#xff1a;element 的版本不够 解决&#xff1a;运行下面两个命令 npm uninstall element-ui //卸载之前安装的版本 npm i element-ui -S //重新安装解决参考&#xff1a;https://blog.csdn.net/weixin_59769148/article/details/1…

vector+算法sort与list+sort的效率比较,容易写错的地方原因探析

我写的代码&#xff1a; #include <iostream> using namespace std; #include <vector> #include <list> #include <algorithm> int main() {const int N 10000000;vector<int> v;list<int> l;for (int i 0; i < N; i){v.push_back(…

SpringCloud Gateway网关梳理

前言 在中大型系统中&#xff0c;Gateway网关发挥着关键的作用&#xff0c;Gateway可以运用在许多应用场景&#xff0c;如接口限流、日志监控、授权认证等等。下面对过往的Gateway知识做一个归纳与总结。 一、路由 路由是Gateway网关中的基础组件&#xff0c;它由一个org.sp…