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掉线重连后ÿ…...
新手入门指南,五分钟完成Taotoken账号注册与第一个API调用
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 新手入门指南,五分钟完成Taotoken账号注册与第一个API调用 对于初次接触大模型API的开发者来说,如何快速上…...
Nodejs后端服务如何集成Taotoken提供稳定的AI功能接口
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Node.js 后端服务如何集成 Taotoken 提供稳定的 AI 功能接口 在构建现代后端服务时,集成大模型能力已成为提升应用智能…...
C++Stack栈类模版实例详解
栈的实现方式分为3种基于静态数组实现,内部预设一个很大的数组对象, 实现简单,缺点是空间受限。基于动态数组实现,内部预设一个容量值,然后分配一段内存空间数组,如果入栈大于默认容量值时,则再次扩大分配新的内存数组,并将旧数组拷贝至新数组及释放旧数组.基于双向…...
Midjourney色调分离终极手册(仅限Pro用户内部流通的17个未公开--no--参数组合)
更多请点击: https://codechina.net 第一章:Midjourney色调分离的核心原理与视觉语义边界 色调分离(Tonal Separation)在 Midjourney 并非原生参数,而是通过提示词工程、风格化权重控制与隐式潜在空间引导协同实现的视…...
酷安UWP桌面客户端:在Windows电脑上高效刷酷安的完整指南
酷安UWP桌面客户端:在Windows电脑上高效刷酷安的完整指南 【免费下载链接】Coolapk-UWP 一个基于 UWP 平台的第三方酷安客户端 项目地址: https://gitcode.com/gh_mirrors/co/Coolapk-UWP 还在为手机小屏幕刷酷安而感到眼睛酸痛吗?想在27寸大屏幕…...
明日方舟基建管理神器:Arknights-Mower 智能助手完整指南
明日方舟基建管理神器:Arknights-Mower 智能助手完整指南 【免费下载链接】arknights-mower 《明日方舟》长草助手 项目地址: https://gitcode.com/gh_mirrors/ar/arknights-mower 每天花半小时手动调整干员排班,计算心情值,安排宿舍休…...
保姆级教程:用Python手把手复现FastICA算法,搞定信号盲分离
从零实现FastICA:Python实战信号盲源分离 想象一下,你正站在一个嘈杂的鸡尾酒会现场,四周环绕着此起彼伏的交谈声、玻璃杯碰撞声和背景音乐。神奇的是,人类大脑能够自动聚焦于特定对话——这种能力在信号处理领域被称为"盲源…...
Linux mkdir、rmdir 命令详解——目录的创建与删除(新手零踩坑)
前言在Linux操作中,目录是文件的“容器”,想要管理文件,首先要学会创建和删除目录。mkdir(创建目录)和rmdir(删除目录)是最基础的目录操作命令,用法简单但有细节,尤其是r…...
InfluxDB Studio:如何用一款工具解决时间序列数据库管理的三大痛点
InfluxDB Studio:如何用一款工具解决时间序列数据库管理的三大痛点 【免费下载链接】InfluxDBStudio InfluxDB Studio is a UI management tool for the InfluxDB time series database. 项目地址: https://gitcode.com/gh_mirrors/in/InfluxDBStudio 时间序…...
港澳通行证照片怎么手机拍?2026 手机拍摄规格要求和实用方法全解
准备办理港澳通行证却被照片规格搞得不知所措?其实用手机就能拍出符合要求的证件照,关键是掌握正确的拍摄方法和规格标准。这篇文章将详细讲解港澳通行证照片的手机拍摄方法,包括规格要求、拍摄步骤,以及如何后期处理让照片完美达…...
