JavaScript 反射(Reflect)和代理(Proxy)简单介绍

1 代理(Proxy)

1.1 代理基本定义

        MDN定义:Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。其基本语法如下,target就是要创建代理的对象,handler就是拦截代理操作的对象,里面定义函数拦截:

const p = new Proxy(target, handler)

        在我看来代理就是在对对象进行操作的时候进行拦截,做一些自己想做的事情,包括一些副作用,例如操作虚拟DOM。

1.2 代理基本示例与应用

1.2.1 基本示例(set get拦截)

        定义obj对象并创建其代理对象,拦截对对象值的访问以及赋值操作,set拦截赋值操作,传入三个参数(不关注receiver参数),分别是目标对象(我们代理的原始对象),prop属性值,以及要赋值的值(=操作符右侧的值)。get方法拦截对对象属性的值,(target, prop)参数,target为我们代理的原始对象,prop是要求访问的对象,代码如下所示:

const obj = {name: 'billy',age: 30
}const objProxy = new Proxy(obj, {// set传入参数:目标对象、属性名、属性值set(target, prop, val){console.log(`Setting value ${val} to ${prop}`)target[prop] = val},// get传入参数:目标对象、属性名get(target, prop){console.log(`Getting value ${prop}`)return target[prop]}
})objProxy.name = 'billy2'
objProxy.age
// output:
// Setting value billy2 to name
// Getting value age

1.2.2 无操作的转发代理

        在创建代理对象的时候,设置任何拦截的函数时,会自动帮助我们进行无任何操作的转发,相当于操作原始对象,代码如下所示:

// 无拦截操作的代理对象const obj = {name: "Billy"
}// 不进行如何拦截操作
const p = new Proxy(obj, {})p.name = "Jack"
p.age = 11
console.log(obj) //{ name: 'Jack', age: 11 }

1.2.3 代理set拦截-进行副作用操作-操作DOM节点

        场景如下:将obj对象的str属性渲染到div标签下,要求就是在改变属性值的时候,div标签下的值也同时改变,实现一个按钮,点击后显示文字内容“Hello World”。

        实现分析:使用set方法进行拦截,同时进行DOM操作,index.html代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="text"></div><button id="btn">Click</button><script src="./15_01_proxy_reflect_.js"></script>
</body>
</html>

        实现代理对象的代码如下所示:

const obj = {str: "Hello World"
}const divNode = document.getElementById('text')
const btn = document.getElementById('btn')const p = new Proxy(obj, {set(target, prop, val){// 进行副作用操作divNode.innerText = valtarget[prop] = va;}
})btn.addEventListener('click', () => {p.str = "Hello World"
})

1.2.4 代理set拦截-赋值类型正确性验证

        要求如下:要求obj对象的age属性始终为number属性,如果不正确抛出异常,实现代码如下所示:

// 代理操作-验证赋值类型正确性
const obj = {name: "Bill",age: 11
}const p = new Proxy(obj, {set(target, prop, val){if(typeof val !== "number"){throw new TypeError("obj.name can't assigned with not a number")}if(val <= 0){throw new RangeError("age range error")}target[prop] = val}
})p.age = 124
// p.age = -199
p.age = "afaf"

2 反射(Reflect)

        MDN定义及描述:Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。与大多数全局对象不同 Reflect 并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。

        我的理解:Reflect实际上就是将JavaScript一些全局函数实现的局部元方法暴露出来,如果使用js提供的全局函数,除了执行Reflect的元操作意外还会有其它操作及判断,Reflect其实就是将某写js操作的核心部分给暴露出来,总结就是以下两点:

  • 通过Reflect方法可以轻松简洁的调用默认行为(赋值、删除、取值等),没有副作用。
  • 与Proxy配合保持一致性

        例如Reflect.get和Reflect.set函数就是js在赋值和取值过程中的核心方法暴露出来(还有一些其他处理没有包括)。

        让我们看以下下面这个例子:

// Reflect的使用
const obj = {a: 1,b: 2,get c(){return this.a + this.b}
}const p = new Proxy(obj, {get(target, prop){console.log(`Getting value ${prop}`)return target[prop]}
})console.log(p.c)
// output:
// Getting value c
// 3

        发现在访问obj.c的时候访问a和b属性没有经过拦截器,接下来使用拦截器进行拦截,代码及结果如下所示(get还有第三个变量reiceiver,其是代理对象本身的引用):

// Reflect的使用
const obj = {a: 1,b: 2,get c(){return this.a + this.b}
}const p = new Proxy(obj, {// receiver为代理对象的引用get(target, prop, receiver){console.log(`Getting value ${prop}`)// 原始使用target[c]无法触发getter// Reflect.get(target, prop, receiver)中的receiver可以保证调用函数的this指向代理对象return Reflect.get(target, prop, receiver)}
})console.log(p.c)
// output:
// Getting value c
// Getting value a
// Getting value b
// 3

        Reflect对象提供的方法包括:

  • Reflect.apply(target, thisArgument, argumentsList)
  • Reflect.construct(target, argumentsList[, newTarget])
  • Reflect.get(target, propertyKey[, receiver])
  • Reflect.set(target, propertyKey, value[, receiver])
  • Reflect.has(target, propertyKey)
  • Reflect.deleteProperty(target, propertyKey)
  • Reflect.defineProperty(target, propertyKey, attributes)
  • Reflect.getOwnPropertyDescriptor(target, propertyKey)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype) Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.ownKeys(target)

2.1 反射基本示例

2.1.1 获取和设置对象属性-Reflect.set-Reflect.get

const obj = {name: "Bill",age: 11
}Reflect.set(obj, "name", "Jack")
console.log(obj.name) // Jack
const res = Reflect.get(obj, "name") // Jack
console.log(res)

2.1.2 检查属性是否存在-Reflect.has

const obj = {name: "Bill",age: 11
}console.log(Reflect.has(obj, "name")) // true
console.log(Reflect.has(obj, "number")) // false

2.1.3 删除属性-Reflect.deleteProperty

const obj = {name: "Bill",age: 11
}Reflect.deleteProperty(obj, "name") 
console.log(obj) // { age: 11 }

3 反射和代理配合使用

        以属性访问日志为例子(即访问属性或者对对象属性进行赋值操作时进行打印),代码如下:

const loggingHandler = {get(target, property) {console.log(`正在访问属性:${property}`);// 调用默认的获取属性行为return Reflect.get(target, property);},set(target, property, value) {console.log(`正在设置属性:${property} = ${value}`);// 调用默认的设置属性行为return Reflect.set(target, property, value);}
};const obj = { a: 1 };
const proxyObj = new Proxy(obj, loggingHandler);console.log(proxyObj.a); // 输出日志并返回值
proxyObj.b = 2; // 输出日志并设置属性

         这里我就产生了疑问,那么这和我直接操作target对象有什么区别吗?有区别,区别如下所示:

  • 返回值的不一致:例如Reflect.set 返回一个布尔值,表示属性是否成功设置。直接操作target设置属性会导致没有明确的返回值,隐式返回被赋的值。
  • 处理this上下文:Reflect.get 接受第三个参数 receiver,用于正确绑定 this 上下文,特别是在访问继承的属性或访问器属性时。直接访问 target[property],不支持传递 receiver,可能导致 this 上下文不正确,尤其是在使用继承或访问器属性时。
  • 遵循 Proxy 捕获器的规则:Proxy和Reflect对象方法一致,确保整个Proxy代理的行为一致性。

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

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

相关文章

《PMI-PBA认证与商业分析实战精析》 第3章 需要评估

本章涵盖的考试重点&#xff1a; 需要评估的四项活动 需要评估四项活动的可交付成果 需要评估相关活动的技术 商业论证的内容 情境说明书的格式 目的、目标和商业论证的层次结构 成本收益分析的四种财务计价方法 需要评估领域就是聚焦在目标定义上。 商业分析师所需要…

网络通信——OSPF协议(基础篇)

这里基础是因为没有讲解OSPF中的具体算法过程&#xff0c;以及其中很多小细节。后续会更新。 目录 一.OSPF的基础信息 二.认识OSPF中的Router ID 三.OSPF中的三张表 四.OSPF中的度量方法&#xff08;计算开销值&#xff09; 五. OSPF选举DR和BDR&#xff08;就是这个区域…

P3131 [USACO16JAN] Subsequences Summing to Sevens S Python题解

[USACO16JAN] Subsequences Summing to Sevens S 题目描述 Farmer John’s N N N cows are standing in a row, as they have a tendency to do from time to time. Each cow is labeled with a distinct integer ID number so FJ can tell them apart. FJ would like to ta…

咸鱼sign逆向分析与爬虫实现

目标&#xff1a;&#x1f41f;的搜索商品接口 这个站异步有点多&#xff0c;好在代码没什么混淆。加密的sign值我们可以通过搜索找到位置 sign值通过k赋值&#xff0c;k则是字符串拼接后传入i函数加密 除了开头的aff…&#xff0c;后面的都是明文没什么好说的&#xff0c;我…

Linux安装RabbitMQ安装

1. RabbitMQ介绍 1.1 RabbitMQ关键特性 异步消息传递&#xff1a;允许应用程序在不直接进行网络调用的情况下交换消息。 可靠性&#xff1a;支持消息持久化&#xff0c;确保消息不会在系统故障时丢失。 灵活的路由&#xff1a;支持多种路由选项&#xff0c;包括直接、主题、…

学习记录:js算法(四十九):二叉树的层序遍历

文章目录 二叉树的层序遍历网上思路队列循环 总结 二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 图一&#xff1a; 示例 1&#xff1a;如图一 输入&#xff1a;roo…

线性代数书中求解齐次线性方程组、非齐次线性方程组方法的特点和缺陷(附实例讲解)

目录 一、克拉默法则 1. 方法概述 2. 例16(1) P45 3. 特点 (1) 只适用于系数矩阵是方阵 (2) 只适用于行列式非零 (3) 只适用于唯一解的情况 (4) 只适用于非齐次线性方程组 二、逆矩阵 1. 方法概述 2. 例16(2) P45 3. 特点 (1) 只适用于系数矩阵必须是方阵且可逆 …

链表OJ经典题目及思路总结(一)

目录 前言1.移除元素1.1 链表1.2 数组 2.双指针2.1 找链表的中间结点2.2 找倒数第k个结点 总结 前言 解代码题 先整体&#xff1a;首先数据结构链表的题一定要多画图&#xff0c;捋清问题的解决思路&#xff1b; 后局部&#xff1a;接着考虑每一步具体如何实现&#xff0c;框架…

CSP-J模拟赛(1)补题报告

前言&#xff1a; 1.交替出场&#xff08;alter) &#xff1a;10 2.翻翻转转&#xff08;filp)&#xff1a;0 3.方格取数&#xff08;square&#xff09;&#xff1a;0 4.圆圆中的方方&#xff08;round)&#xff1a;0 总结一下&#xff1a; 第一次考&#xff0c;没爆零就是胜…

Java面试必杀技为什么面试官都爱问源码?

你也许能说出一万个不知道原理源码也能胜任工作的理由。但是也改变不了&#xff0c;高质量的人才必须要通过原理源码来筛选的事实&#xff01; 不要抱怨没有时间学习&#xff0c;去年到今年&#xff0c;一年时间过去了&#xff0c;你是没时间学习&#xff0c;还是有时间也没学习…

大数据毕业设计选题推荐-个性化图书推荐系统-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

螺狮壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习01(环境准备)

1 准备工作 由于创建数据中心需要安装很多服务器&#xff0c;这些服务器要耗费很所物理物理计算资源、存储资源、网络资源和软件资源&#xff0c;作为穷学生只有几百块的n手笔记本&#xff0c;不可能买十几台服务器来搭建数据中心&#xff0c;也不愿意跑实验室&#xff0c;想躺…

MySQL基础篇 - 多表查询

01 多表关系 【1】概念&#xff1a;项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各表结构之间也存在着各种联系&#xff0c;基本上分为三种…

音视频入门基础:FLV专题(10)——Script Tag实例分析

一、引言 在《音视频入门基础&#xff1a;FLV专题&#xff08;9&#xff09;——Script Tag简介》中对FLV文件的Script Tag进行了简介。下面用一个具体的例子来对Script Tag进行分析。 二、Script Tag的Tag header实例分析 用notepad打开《音视频入门基础&#xff1a;FLV专题…

超分服务的分量保存

分量说明 分量的概念主要是对于一个显卡和网络传输而言&#xff0c;显卡可以同时进行几个线程&#xff0c;多个显卡可以分布式进行量的同时进行AI识别&#xff0c;比如我们有cuda的显卡&#xff0c;cuda的核心量可以分给不同的分片视频&#xff0c;第一步先将视频减小&#xff…

Java 自定义异常及经验小结

1&#xff0e;java内置的异常类可以处理大部分异常情况。此外&#xff0c;用户还可以自定义异常&#xff0c;只需继承Exception类即可。 2&#xff0e;在程序中使用自定义异常类&#xff0c;大体可分为以下几个步骤&#xff1a; &#xff08;1&#xff09;创建自定义异常类 &…

VBA数据库解决方案第十五讲:Recordset集合中单个数据的精确处理

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

虚拟机、ubantu不能连接网络,解决办法

虚拟机、ubantu不能连接网络&#xff0c;解决办法 物理机OS&#xff1a; [Windows10 专业版](https://so.csdn.net/so/search?qWindows10 专业版&spm1001.2101.3001.7020) 虚拟机平台&#xff1a; VMware Workstation 16 Pro 虚拟机OS&#xff1a; Ubuntu 18.04 自动配…

适合初学者的[JAVA]: 基础面试题

目录 说明 前言 String/StringBuffer/StringBuilder区别 第一点: 第二点: 总结&#xff1a; 反射机制 JVM内存结构 运行时数据区域被划分为5个主要组件&#xff1a; 方法区&#xff08;Method Area&#xff09; 堆区&#xff08;Heap Area&#xff09; 栈区&#x…

SSM整合:图书管理系统

图书管理系统 一.环境 1.数据库环境 CREATE DATABASE ssmbuild;USE ssmbuild;DROP TABLE IF EXISTS books;CREATE TABLE books (bookID INT(10) NOT NULL AUTO_INCREMENT COMMENT 书id,bookName VARCHAR(100) NOT NULL COMMENT 书名,bookCounts INT(11) NOT NULL COMMENT 数量…