JavaScript 中如何代理 Set(集合) 和 Map(映射)

news2025/1/9 20:07:26
ECMAScript6 中 Set 和 Map 的代理方法

上一节:《JavaScript 中如何代理数组 | 下一节:《JavaScript 中的反射(Reflect)原理与应用

今日正在编写中,未完待续…

jcLee95
邮箱 :291148484@163.com
CSDN 主页https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343
本文地址https://blog.csdn.net/qq_28550263/article/details/128439767

目 录

1. 概述

  • 1.1 与代理普通对象的区别
  • 1.2 Set (集合)的原型属性和方法
  • 1.3 Map (映射)的原型属性和方法

2. 代理的实现思路

  • 2.1 从一个错误说起
  • 2.2 代理一般原型方法
  • 2.3 响应式代理原理与实现

1. 概述

1.1 与代理普通对象的区别

Set 和 Map 与普通对象的一个区别是他们具有普通对象没有的属性和方法。代理 Set 和 代理 Map 的思路基本一致,只不过比代理普通对象要麻烦了许多。关键在于你需要完成 Set 和 Map 各具体原型方法的代理实现。因此,在实现对 Set 和 Map 的代理之前,我们需要大略过一下 Set 和 Map 的原型属性与方法。

1.2 Set (集合)的原型属性和方法

原型方法/属性描述
Set.prototype.add()如果 Set 对象中没有具有相同值的元素,则 add() 方法将插入一个具有指定值的新元素到 Set 对象中。
Set.prototype.clear()该方法移除 Set 对象中所有元素。
Set.prototype.delete()该方法从 Set 对象中删除指定的值(如果该值在 Set 中)。
Set.prototype.entries()该方法返回一个新的迭代器对象,这个对象包含的元素是类似 [value, value] 形式的数组,value 是集合对象中的每个元素,迭代器对象元素的顺序即集合对象中元素插入的顺序。
Set.prototype.forEach()该方法对 Set 对象中的每个值按插入顺序执行一次提供的函数。
Set.prototype.has()该方法返回一个布尔值来指示对应的值是否存在于 Set 对象中。
Set.prototype.keys()该方法是 values() 方法的别名。
Set.prototype.values()该方法返回一个新的迭代器对象,该对象按插入顺序包含 Set 对象中每个元素的值。
Set.prototype[@@iterator]()@@iterator 属性的初始值和 values 属性的初始值是同一个函数。
Set.prototype.size该属性将会返回 Set 对象中(唯一的)元素的个数。
get Set[@@species]该访问器属性返回Set的构造函数。

1.3 Map (映射)的原型属性和方法

温馨提示:Map 在编程语言中不叫 地图,而是 映射。

原型方法/属性描述
Map.prototype.clear()该方法会移除 Map 对象中的所有元素。
Map.prototype.delete()该法用于移除 Map 对象中指定的元素。
Map.prototype.entries()该方法返回一个新的迭代器对象,其中包含 Map 对象中按插入顺序排列的每个元素的 [key, value] 对。
Map.prototype.forEach()该方法按照插入顺序依次对 Map 中每个键/值对执行一次给定的函数。
Map.prototype.get()该方法从 Map 对象返回指定的元素。
Map.prototype.has()该方法返回一个布尔值,指示具有指定键的元素是否存在。
Map.prototype.keys()该返回一个引用的迭代器对象。它包含按照顺序插入 Map 对象中每个元素的 key 值。
Map.prototype.set()该方法为 Map 对象添加或更新一个指定了键(key)和值(value)的(新)键值对。
Map.prototype.values()该方法返回一个新的迭代器对象。它包含按顺序插入 Map 对象中每个元素的 value 值。
Map.prototype[@@iterator]()@@iterator 属性的初始值与 entries 属性的初始值是同一个函数对象。
Map.prototype.size该属性返回 Map 对象的成员数量。
get Map[@@species]该访问器属性会返回一个 Map 构造函数。

2. 代理的实现思路

2.1 从一个错误说起

我们想代理一个 Set 对象实例,于是这样做了:

const s = new Set([1, 2]);
const s_proxy = new Proxy(s, {})

接着我们尝试通过代理对象 s_proxy 获取 s 的 size:

s_proxy.size

这时错误产生了:
在这里插入图片描述
上面的报错意思是说:在不兼容的接收器 #<Set >上调用了方法 get Set.prototype.size
这表明,我们直接想要不定义任何东西使用Proxy来代理 Set,首先在 size 属性上就没有成功。为什么的?

这是因为 Set.prototype.size 是一个 get 访问器属性(它的 set 存储器属性未定义)。当调一个Set用该属性时:

因此我们需要在船舰代理对象时增加 getter 拦截方法,并使访问器属性 size 的 getter 函数执行时, this 指向被代理的 Set 对象实例,而不是 代理对象自己:

const s = new Set([1, 2]);
const s_proxy = new Proxy(s, {
  get(target, key, receiver) {
    // 对于 size 属性
    if(key === 'size') {
      // 返回 target['size'], target 表示被代理的目标对象
      return Reflect.get(target, key, target)
    }
    // 其它属性
    else{
      // 仍返回 receiver[key]
      // receiver表示 Proxy 或者继承 Proxy 的对象
      return Reflect.get(target, key, receiver)
    }
  }
})

【注】:
Reflect.get 方法就如同属性访问器语法(target[propertyKey]) 从对象中读取属性,只不过 Reflect.get 方法 是通过一个函数执行来操作的:

Reflect.get(target, propertyKey[, receiver])
  • target: 需要取值的目标对象
  • propertyKey: 需要获取的值的键值
  • receiver: 如果target对象中指定了getter,receiver则为getter调用时的this值
    返回属性的值。

现在,我们的代理对象上访问 size 就不会报错了:
在这里插入图片描述

2.2 代理一般原型方法

很快你就意识到,在代理对象 s_proxy 上同样无法使用 add()、clear()、delete() 等等 Set 的原型方法。

很显然,这个问题和 访问器属性 size 不那么一样:
由于 size 是一个Set实例上的访问器属性,我们要使用 s_proxy.size 只需要通过修改 receiver 来改变访问器 getter 函数的 this 指向。
但是 add() 这些函数不像调用 s_proxy.size 会自动执行 getter,不论如何修改 receiver ,访问 s_proxy.add 对应的 add() 方法并没有执行(而访问 s_proxy.set 对应的 get(...)方法会执行 ),因此这些方法执行时的 this 仍然指向着 代理对象 s_proxy而不是被它所代理的 s
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
因此我们的目标还是在像一个办法,当执行这些方法是将方法与原始数据对象target绑定。

const s = new Set([1, 2]);
const s_proxy = new Proxy(s, {
  get(target, key, receiver) {
    if(key === 'size') {
      return Reflect.get(target, key, target)
    }
    else{
      return target[key].bind(target);
    }
  }
})

在这里插入图片描述

当调用的是方法时,target[key] 返回就是代理对象上名为 key 的属性,如果是方法则返回的是方法。比如,访问代理对象上的 add 方法时:

s_proxy.add(3)

target[key] 返回的是 ƒ add() { [native code] },也就是 add 函数。

因此,target[key].bind(target);也就是将这个 add() 函数绑定到 target(被代理对象)上。换句话说就是将 target[key] 返回的函数的 this 指向原始对象 target

【注】
Function.prototype.bind()方法
该方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
其语法格式为:

function.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisArg:调用绑定函数时作为 this 参数传递给目标函数的值。
  • arg1, arg2, …:当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

返回:返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

可知,不仅是 add 方法,其它的方法如 delete、clear 等等,一旦访问的时候后悔这样绑定到原始对象上执行。

不仅是 Set,Map也可以通过类似的思路完成代理。

2.3 响应式代理原理与实现

编写中,尚未完成…

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

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

相关文章

Git分支操作

实操记录 假定非管理人员操作&#xff1a; 直推&#xff1a; 新建特性分支cbry&#xff1a; 刷新分支&#xff1a; checkout切换&#xff1a; 本地文件查看&#xff1a; 再merge&#xff1a; 就此&#xff0c;master的代码就合并到特性分支cbry&#xff1a; 新增内容&#xff…

数字化技术转型

这篇老生常谈&#xff08;我写过N次&#xff09;&#xff0c;是应一位IM群中的朋友的困惑问答汇集而成的。&#xff08;1&#xff09;学科分类我上学学的是计算机系。我上的大学一开始并没有计算机系&#xff0c;后来是电子工程系和数学系的老师抽调组成了计算机系。后来&#…

申请大学用的是IB预估分?

IB课程体系以其独特的优越性成为越来越多国际高中生的选择。如今全球共有3300多所高校接受IB成绩申请&#xff0c;其中包括美国常春藤盟校、英国G5在内的多所名校。 但是&#xff0c;大家知道吗&#xff0c;国内学习IB课程的学生是需要用预估分来申请大学的。今天&#xff0c;小…

多用户及时通信系统

目录1. QQ用户登录1.1 用户登录11.2 用户登录21.3 用户登录32. 拉取在线用户3. 无异常退出4. 私聊系统5. 群聊3. 发送文件3.1 服务端推送新闻3.2 离线留言和离线发文件1. QQ用户登录 1.1 用户登录1 qqcommon包下 User类序列化 Message消息类序列化 MessType接口 qqclient.ut…

拉伯杠杆平台|沪指上涨,大金融板块领涨,有股票连续5涨停!

A股周二上午全体小幅上涨&#xff0c;大金融集体上涨&#xff0c;推动指数上行&#xff0c;商场全体动摇不大。A50期货高开高走&#xff0c;盘中暴拉超2.6%。 不过&#xff0c;部分个股仍然动摇不小&#xff0c;有多只股票接连涨停。 别的&#xff0c;新股持续分解&#xff0c…

ATAC-seq分析:数据介绍(2)

1. 简介 ATACseq (Assay for Transposase-Accessible Chromatin using sequencing) 使用转座酶在测序前有效地片段化可访问的 DNA&#xff08;DNA可极性&#xff09;。结果提供了一种绘制可访问/开放染色质基因组范围的方法。 与其他技术相比&#xff0c;ATACseq 有几个优点&am…

嵌入式开发学习之--串口通讯(下)

提示&#xff1a;本篇来做一个关于串口的输入输出实验。 文章目录前言一、项目概况1.1、项目需求1.2、项目来源1.3、开发环境1.4、项目意义1.5、项目效果展示二、开发步骤2.1、涉及硬件电路2.2、项目代码2.2.1、串口配置总结前言 前一篇文章我们介绍了串口的几种类型以及串口标…

Linux Shell 编程,运算符,条件与分支,循环

Linux Shell 编程&#xff0c;运算符&#xff0c;条件与分支&#xff0c;循环1.Shell运算符2.判断语句3.for循环4.while循环1.Shell运算符 学习如何在shell中进行各种运算操作 案例&#xff1a;计算&#xff08;57&#xff09;3的值&#xff1a; #!/bin/bash res$(((57)*3)) …

游戏物体GameObject

在unity中所有游戏物体都是GameObject&#xff0c;这也是编程中的对象。 创建物体 在hierarchy窗口中&#xff0c;右击可以创建一个物体&#xff0c;当然也可以创建空物体。 物体属性 创建完物体后&#xff0c;一般可以在此处用这些工具来改变物体。 移动&#xff0c;旋转和…

戴尔科技集团助力中国石油大学打造现代数据中心

小的时候      总是幻想着      能够躺在床上上课      没想到现在竟然实现了      没错,对于当代大学生尤其是19级、20级来说,大学生活似乎是个虚无缥缈的词汇,因为相比与在校生活,在家上网课的时间可能会更长,一不留神就上了三年的“家里蹲”。      即使…

安装配置高度安全的匿名操作系统,利用暗网情报数据抓取工具获取普通人根本查看不到的信息

安装配置高度安全的匿名操作系统,利用暗网情报数据抓取工具获取普通人根本查看不到的信息。 Whonix匿名操作系统,Whonix 是一个专注于匿名,隐私和安全的操作系统。它基于Tor匿名网络,Debian GNU / Linux和隔离安全性。DNS泄漏是不可能的,即使具有root权限的恶意软件也无法…

“多点”开花,独立走向新零售

12月7日&#xff0c;亚洲最大的数字零售服务商多点Dmall正式向港交所递交招股说明书&#xff0c;在零售行业逐渐向线上线下一体化、店仓一体模式迈进之时&#xff0c;多点Dmall成为很多传统零售商转型路上的首选合作伙伴&#xff0c;给予了资本市场一定想象空间。 但也有观点认…

【Lua】ToLua逻辑热更新

1 前言 Lua基础语法 中系统介绍了 Lua 的语法体系&#xff0c;本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新。 逻辑热更新是指&#xff1a;在保持程序正常运行的情况下&#xff0c;在后台修改代码逻辑&#xff0c;修改完成并推送到运行主机上&#xff0c;主机无缝接入…

HarmonyOS原子化服务最新概念、呈现形式与触发方式

一、HarmonyOS原子化服务最新概念 总体介绍&#xff1a; 原子化服务&#xff08;Ability&#xff09;是由HarmonyOS服务开放平台接入的一种技能&#xff0c;为华为智慧服务、智慧搜索、服务直达、智慧语音以及智慧视觉等流量入口提供服务技能。原子化服务以轻量化的呈现形式&…

高级网络应用复习——TCP与UDP,ACL列表, 防火墙,NAT复习与实验(带命令)

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.知识点总结 1.传输层的协议 &#xff08;1&#xff09;TCP 和…

「实操」结合图数据库、图算法、机器学习、GNN 实现一个推荐系统

本文是一个基于 NebulaGraph 上图算法、图数据库、机器学习、GNN 的推荐系统方法综述&#xff0c;大部分介绍的方法提供了 Playground 供大家学习。 基本概念 推荐系统诞生的初衷是解决互联网时代才面临的信息量过载问题&#xff0c;从最初的 Amazon 图书推荐、商品推荐&…

TCP的三次握手四次挥手详解

想要了解TCP的三次握手和四次挥手&#xff0c;首先要了解TCP的头部结构 TCP的头部结构如下 16位源端口号&#xff1a; 客户端地址信息16位目标端口号&#xff1a; 服务端地址信息32位序列号&#xff1a; 请求报文端数据流子节开始的位置&#xff08;比如位1024&#xff5e;2…

【20天快速掌握Python】day15-网络编程

1.网络通信的概念 简单来说&#xff0c;网络是用物理链路将各个孤立的工作站或主机相连在一起&#xff0c;组成数据链路&#xff0c;从而达到资源共享和通信的目的。 使用网络的目的&#xff0c;就是为了联通多方然后进行通信&#xff0c;即把数据从一方传递给另外一方。 前…

以分页场景谈MVC设计模式

一 、需求场景 需要实现一个分页组件&#xff0c; 可以方便的进行分页操作。 二、分析需求 从分页需求出发&#xff0c;分析潜在的元素&#xff0c; 虽然只包含一个大的分页功能&#xff0c;但是潜在的元素 包含&#xff1a;上一页 下一页 首页 尾页 当前页 等等。 为什么包含…

【Oauth2】SpringBoot整合Oauth2实现认证授权

SpringBoot整合Oauth2实现认证授权 应用场景 OAuth2.0 协议的使用场景主要为&#xff1a;第三方登录和开放接口的调用 第三方登录就是使用微信等第三方的方式来登录一个应用或者网站&#xff0c;比如用微信账号登录gitee。 而开发接口的调用&#xff0c;则比如说微信、美团…