ES6代理和反射新特性,详细讲解
代理与反射
es6新增了代理和反射特性,这两个特性为开发者提供了拦截并向基本操作嵌入额外行为的能力。
代理基础
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Title</title></head><body><h2>代理与反射</h2></body><script>const target = {id: 'target'}const handle = {}const proxy = new Proxy(target,handle)console.log(proxy.id)console.log(target.id)</script>
</html>
代理是目标对象的抽象。 所以直接操作代理和直接操作对象,所操作的值都会映射到代理对象上。
代理对象每次执行某个操作(读属性、写属性、定义新属性、查询原型、把它作为函数调用)时,它只会把相应操作发送给处理器对象或目标对象。
Proxy构造器接收两个参数,目标对象和处理器对象。如果处理器对象上存在对应方法,代理就调用该方法执行相应操作。如果处理器对象上不存在对应方法,则代理就在目标对象上执行基础操作。

定义捕获器
使用代理的主要目的是可以定义捕获器。当定义捕获器后,在代理对象上调用基本操作时(比如新增、删除、修改),代理可以在运行这些操作之前调用捕获器,从而拦截并修改相应的行为。
下面看一个例子
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Title</title>
</head>
<body><h2>代理与反射</h2>
</body>
<script>const target = {id: 'target'}const handle = {get(){console.log('捕获器运行')return '这是一个捕获器'}}const proxy = new Proxy(target,handle)console.log(proxy.id)console.log(target.id)
</script>
</html>

在访问代理对象的属性时,会出发get操作(捕获器函数),但是在原始对象上操作是不会触发的。否则就应该打印两次了。
捕获器函数可以接收三个参数,分别是目标对象,要查询的属性以及代理对象。
那如果我们想在捕获函数内部调用原始对象上的行为呢?Reflect对象为我们提供了这个能力。
下面看一个例子
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Title</title>
</head>
<body><h2>代理与反射</h2>
</body>
<script>const target = {foo: 'bar'}const handle = {get(){return Reflect.get(...arguments)}}const proxy = new Proxy(target,handle)console.log(proxy.foo)console.log(target.foo)
</script>
</html>

可以看到值是一样的,那么我们利用这个属性就可以在操作对象时给对象的属性添加一些额外的东西,比如
const target = {foo: 'bar'
}
const handle = {get(){return Reflect.get(...arguments) + '帅'}
}
const proxy = new Proxy(target,handle)
console.log(proxy.foo)
console.log(target.foo)

捕获器的一些限制
在使用捕获器时,需要遵守"捕获器不变式"。原因是因为:如果一个捕获器不遵守捕获器不变式,可能会出现过于反常的行为! 下面来看一个例子:
如果目标对象有一个不可配置且不可写的数据属性,那么在捕获器返回一个与该属性不同的值时,会抛出TypeError
const target = {}
Object.defineProperty(target,'foo',{// 不可配置configurable: false,// 不可写writable: false,value: 'bar'
})
const handle = {get(){return Reflect.get(...arguments) + '帅'}
}
const proxy = new Proxy(target,handle)
console.log(proxy.foo)
console.log(target.foo)

那捕获器函数返回一个一样的值呢?

实测返回一样的值不会报错。
撤销代理
很简单,调用一个revocable方法。有想要了解具体的可以看看MDN的介绍。
Proxy.revocable() - JavaScript | MDN
看一段代码就知道怎么使用了
const target = {}
Object.defineProperty(target,'foo',{// 不可配置// configurable: false,// // 不可写// writable: false,value: 'bar'
})
const handle = {get(){console.log('运行')return Reflect.get(...arguments)}
}
const proxy = new Proxy(target,handle)
console.log(proxy.foo)
console.log(target.foo)
const {proxy:proxy1,revoke} = Proxy.revocable(target,handle)
proxy1.foo
revoke()
proxy1.foo

代理的不足
-
this指向问题
-
代理与内部槽位
-
- 有些js内置类型可能会依赖内部槽位进行一些方法的调用,而代理对象是无法访问到这些方法的,就会导致出错。
const target = new Date()
const proxy = new Proxy(target,{})
console.log(proxy instanceof Date)
proxy.getDate()
输出结果:

反射
Reflect对象是一个内置的对象,不可以使用new运算符进行实例化,它可以用自己的方法代替原生的操作对象。就是有一个人和你一模一样,你可以不用亲自做一些事情,这个人就帮你做了。
来看一下例子:
const target = {foo: 'bar'
}
delete target.foo
console.log(target)
这是我们如果想要删除对象上的某个属性,采取的一种做法。
来看下反射如何做
const target = {foo: 'bar'
}
Reflect.deleteProperty(target,'foo')
console.log(target)

在学习过程中我是有一个疑惑的,为什么JS会出现反射呢? 其实它是为了跟Proxy进行配合。
Proxy可以代理对象的一些操作,比如增加、删除、修改。想一下这个场景:如果Proxy代理了新增操作,然后最后想调用原生的赋值方法,该怎么做?
- 自己手动实现一个。
const target = {foo: 'bar'
}
let proxy1 = new Proxy(target,{get(target, p, receiver) {console.log('当前值为::',target[p])return target[p]}
})
console.log(proxy1.foo)

- 使用反射
const target = {foo: 'bar'
}
let proxy1 = new Proxy(target,{get(target, p, receiver) {console.log('当前值为::',target[p])return Reflect.get(target,p,receiver)}
})
console.log(proxy1.foo)

我们无需关心原来操作的实现逻辑,只需要关心我们要为某个操作添加的逻辑即可,原来的逻辑反射会替我们做到。
所以反射的Api是和Proxy捕获器的api一一对应的,当我们想要调用原生操作时,直接使用反射提供的api即可。
总结
从宏观来看,代理是真实JS对象的透明抽象层。在遵循捕获器不变式的前提下,代理可以定义捕获器,可以拦截大部分JS的基本操作和方法。从而增强这些操作。 例如实现装饰器。
反射则封装了一整套与捕获器拦截的操作相对应的方法。可以把反射API看作一套基本操作,包含绝大部分JS对象的API基础。
相关文章:
ES6代理和反射新特性,详细讲解
代理与反射 es6新增了代理和反射特性,这两个特性为开发者提供了拦截并向基本操作嵌入额外行为的能力。 代理基础 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta charset"UTF-8"&g…...
vue计算属性 初步使用案例
<template><div><h1>购物车</h1><div v-for"item in filteredItems" :key"item.id"><p>{{ item.name }} - {{ item.price }} 元</p><input type"number" v-model.number"item.quantity"…...
大模型时代,呼叫中心部门如何建设一套呼出机器人系统?
大模型时代,呼叫中心部门如何建设一套呼出机器人系统? 作者:开源呼叫中心系统 FreeIPCC,Github地址:https://github.com/lihaiya/freeipcc 在大模型时代,呼叫中心部门建设一套呼出机器人系统需要综合考虑技…...
使用Java绘制图片边框,解决微信小程序map组件中marker与label层级关系问题,label增加外边框后显示不能置与marker上面
今天上线的时候发现系统不同显示好像不一样,苹果手机打开的时候是正常的,但是一旦用安卓手机打开就会出现label不置顶的情况。尝试了很多种办法,也在官方查看了map相关的文档,发现并没有给label设置zIndex的属性,只看到…...
力扣 LeetCode 142. 环形链表II(Day2:链表)
解题思路: 使用set判断是否重复添加,如果set加入不进去证明之前到达过该节点,有环 public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();ListNode cur head;while (cur …...
用MVVM设计模式提升WPF开发体验:分层架构与绑定实例解析
MVVM(Model-View-ViewModel)是一种架构模式,广泛应用于现代前端开发,尤其是在微软的WPF(Windows Presentation Foundation)应用程序中。它旨在通过将视图(UI)与业务逻辑(…...
C++中的动态断言和静态断言
C中包含动态断言(assert)和静态断言(static_assert),下面分别分析各自的用法。 1.动态断言(assert) assert 是一个宏,在预处理阶段不生效,在运行阶段才起作用࿰…...
运算放大器的学习(一)输入阻抗
输入阻抗 最近需要对运算放大器进行学习,我们后面逐一对其参数进行了解。 首先了解下输入阻抗。 放大电路技术指标测试示意图: 输入电阻: 从放大电路的输入端看进去的等效电阻称为放大电路的输入电阻,如上图,此处考虑…...
Rust枚举之卧龙凤雏(Rust Option枚举、Rust Result枚举)(Rust Enum、Some(T)、Ok(T)、Err(E))链式操作
文章目录 Rust 枚举之卧龙凤雏枚举的基本概念枚举定义示例 Result 枚举:凤雏Result 枚举的定义Result 的使用场景示例 1:文件读取示例 2:链式操作与错误处理 Option 枚举:卧龙Option 枚举的定义Option 的使用场景示例 1࿱…...
TCP/IP协议,TCP和UDP区别
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/互联网协议)是一组用于计算机网络中的通信协议,它为数据传输提供了标准框架,广泛用于互联网和局域网中。TCP/IP协议包括多个层次,每…...
【go从零单排】Timer、Epoch 时间函数
🌈Don’t worry , just coding! 内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。 📗概念 在 Go 语言中,time.Timer 是一个用于在指定时间后执行操作的计时器。…...
壁仞科技上市前最后一波 校招 社招 内推
随着美国大选结束,国内GPU 产业得到空前的的发展空间,国内芯片相关股票一片飘红。 国内大型 GPU厂商壁仞科技,摩尔线程等正紧锣密鼓地加紧上市。 GPGPU 芯片赛道来到了史无前例的红利点,抓住机会💪 壁仞科技正在火热…...
【微软报告:多模态基础模型】(2)视觉理解
欢迎关注【youcans的AGI学习笔记】原创作品 【微软报告:多模态基础模型】(1)从专家到通用助手 【微软报告:多模态基础模型】(2)视觉理解 【微软报告:多模态基础模型】(3)…...
Linux 驱动
四十三、Linux设备树 43.1 DTS、DTB 和 DTC DTS 是设备树源码文件 DTB 是将DTS 编译以后得到的二进制文件。 DTC 工具将.dts 编译为.dtb 43.2 DTS语法 43.2.1 .dtsi 头文件 在.dts 设备树文件中,可以通过“#include”来引用.h、.dtsi 和.dts 文件。 …...
【数学二】线性代数-线性方程组-齐次线性方程组、非齐次线性方程组
考试要求 1、会用克拉默法则. 2、理解齐次线性方程组有非零解的充分必要条件及非齐次线性方程组有解的充分必要条件. 3、理解齐次线性方程组的基础解系及通解的概念,掌握齐次线性方程组基础解系和通解的求法. 4、理解非齐次线性方程组的解的结构及通解的概念. 5、会用初等行变…...
Git别名设置
在 Git 中设置命令别名可以让你更高效地使用常见的 Git 命令。通过为常用命令创建简短的别名,可以减少输入的字符数并加速工作流程。 参考链接 设置 Git 命令别名的方法: 使用 Git 配置命令: Git 允许通过 git config 命令来设置命令别名。这…...
算法基础 -- 红黑树原理与插入伪代码
红黑树原理与插入伪代码 红黑树的原理 红黑树是一种自平衡的二叉搜索树,通过对节点的颜色(红色或黑色)以及结构的约束条件来保持树的平衡。红黑树的原理可以通过以下五个特性描述: 节点是红色或黑色。根节点必须是黑色。所有叶…...
力扣 LeetCode 27. 移除元素(Day1:数组)
解题思路: 注意:数组只能覆盖,不能删除 erase方法的复杂度为O( n )而不是O( 1 ),因为需要把删除后后面的数组向前移动 方法一:双层for循环暴力 方法二:快慢指针 fast表示新数组的元素 slow表示新数组元…...
微服务链路追踪skywalking安装
SkyWalking是一个开源的分布式追踪系统,主要用于监控和分析微服务架构下的应用性能。 它提供了分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案,特别适用于微服务、云原生架构和基于容器的环境(如Docker、K8s、Mesos&…...
mqtt学习笔记(一)
以解决问题方式逐步学习探索 mqtt使用场景mqtt可能缺点mqtt学习疑问探索1、mqtt主题发布过的历史消息,全新连接的client能消费到吗?2、mqtt的client掉线如何重连,重连后订阅的topic配置还在不?3、mqtt的client掉线重连后ÿ…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
【版本控制】GitHub Desktop 入门教程与开源协作全流程解析
目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork(创建个人副本)步骤 2: Clone(克隆…...
Go 并发编程基础:select 多路复用
select 是 Go 并发编程中非常强大的语法结构,它允许程序同时等待多个通道操作的完成,从而实现多路复用机制,是协程调度、超时控制、通道竞争等场景的核心工具。 一、什么是 select select 类似于 switch 语句,但它用于监听多个通…...
js 比较两个对象的值,不相等就push对象的key
在JavaScript中,比较两个对象(object)的值并找出不相等的key,可以通过多种方法实现。下面是一些常用的方法: 方法1:使用JSON.stringify 这种方法适用于简单的对象,其中对象的值是基本类型或可…...
