proxy的应用实例

习题一

实现下面例子中的效果,需要怎么做?

var arr = ['a', 'b', 'c', 'd', 'e', 'f'];
console.log(a + a.a + a.a.a + a.a.a.a + a.a.a.a.a); // abcdef

看到a + a.a + a.a.a + a.a.a.a + a.a.a.a.a的形式,第一个出现的解决办法就是:Proxy代理。其次就是toString()方法,因为我们最后的结果需要对应数组中的元素,并且为字符串的形式abcdef
特别注意:开始想a + a.a + a.a.a的形式,只要我将结果对应成'a' + 'b' + 'c'的形式不就可以了吗?其实这个想法是错误的,因为如果a.a对应的结果是字符串'b'的话,那么a.a.a对应的结果就会是'b'.a的形式,这样程序肯定是会抛出异常的,因为字符串'b'不存在属性a。所以这样思路是错误的。
正确的思路:数据类型隐式转换,其实a + a.a + a.a.a这样的形式中牵扯着数据计算时的隐式转换问题。什么意思呢?其实就是按照我们上面的思路来处理,虽然我们上面的思路是错误的,但是错误的原因是我们将a + a.a + a.a.a的形式转为字符串的形式相加。如果我们现在让a + a.a + a.a.a的形式对应为{} + {} + {}的形式应该处理?
{} + {} + {}其实本质上就是隐式类型转换的问题,程序会如何进行隐式转换?

  1. 首先对象是无法进行加法计算的,JS会尝试将对象的形式转为原始类型,因为转为原始数据类型才能够运行加法运算。所以程序会首先寻找对象是否存在Symbol.toPrimitive属性,Symbol.toPrimitive可以将对象转为一个原始值。该函数被调用时,会被传递一个字符串参数hint,表示要转换到原始值的预期类型。hint参数的取值是number、string、default中的任意一个。
  2. 如果该对象不存在Symbol.toPrimitive属性的话,那么就要去寻找toString/valueOf方法。特别注意:这里存在一个顺序问题,是先调用toString方法还是先调用valueOf方法?其实这就牵扯到一个更细的知识点:如果是将对象转换为number的话,那么就先调用valueOf方法;如果是将对象转为string类型的话,那么就先调用toString方法。
  3. {} + {} + {}使用的是加法运算,那么自然就会触发number的形式转换,也就是将对象的形式转为number类型。

所以按照上面的说法,将{} + {} + {}进行改造,看看程序是如何执行的:
在每个对象中分别都添加了toString,valueOf方法,为什么str输出的结果是'abc',而不是'123'?特别注意下:并不是说,一看到+运算符就认为程序直接调用toString()方法。在隐式转换类型中,并不是这样的。上面说过,对象转为原始数据类型:那么首先要看Symbol.toPrimitive,如果不存在Symbol.toPrimitive属性的话,那么对象会通过不同的顺序调用valueOftoString方法将其转为原始数据类型。
所以说,我们下面的例子str输出的是字符串'abc'

var str = {
	valueOf() {
		return 'a'
	},
	toString() {
		return '1'
	}
} + {
	valueOf() {
		return 'b'
	},
	toString() {
		return '2'
	}
} + {
	valueOf() {
		return 'c'
	},
	toString() {
		return '3'
	}
};
console.log(str); // abc

明白了对象转为原始值类型的顺序之后就很简单了。a + a.a + a.a.a + a.a.a.a这种形式,我们可以用Proxy代理对象,很显然被代理的对象是a
首先我们要明白Proxy到底有什么用呢?Proxy用于创建一个目标对象的代理对象,从而实现目标对象基本操作的拦截与自定义(如属性查找、赋值、枚举、函数)。
所以下面例子中的变量a就是代理对象,而new Proxy()构造函数中的参数就是目标对象。
目标对象中的参数是什么意思?

  1. values:结果需要对应的数组集合。
  2. step:计步器,既然结果要对应数组中的元素,就需要一个类似计步器的东西。
  3. valueOf/toString:对象转为原始数据类型需要调用的方法,valueOftoString方法都可以,二者只不过是调用的顺序不同。

get函数是什么意思呢?其实get函数就是getter函数,也就是说当对代理对象进行属性读取操作时执行的方法。get函数中的参数对应着什么意思呢?

  1. target:目标对象,也就是需要被代理的对象。(与new Proxy()构造函数的参数相等)
  2. key:读取操作时,获取到的属性名。
  3. receiverProxy或者继承Proxy对象。

为什么我们在读取操作的时候,需要判断key值呢?因为在读取操作的时候,受到一些其它属性的影响,例如key的值可能是Symbol.toPrimitive、valueOf、toString之类的,所以我们要进行排除。当读取操作获取到的属性名不存在目标对象的身上时,此时我们就要返回receiver代理对象。
这样做的目的,是将a + a.a + a.a.a的形式转换为{} + {} + {}的形式,为什么step能够实现不停的自增操作呢?因为我们在get函数中判断key值,当key值满足条件时,返回的是代理对象a引用。也就是说a.a, a.a.a, a.a.a.a指向的都是同一个引用,也就是代理对象a,所以step肯定会自增。

let obj = {
  step:0,
  arr:['a','b','c','d','e','f'],
  valueOf:function(){
    return this.arr[this.step++]
  }
}
let a = new Proxy(obj,{
  get(target, key, receiver){
    if(!Reflect.has(target,key)&& typeof key!=='symbol'){
      return receiver
    }else{
      return target[key]
    }
  }
});
console.log(a + a.a + a.a.a + a.a.a.a + a.a.a.a.a+a.a.a.a.a.a); // abcdef

习题二

console.log(a[10][20] + 30); // 60
console.log(a[100][200] + 300); // 600
console.log(a[200][300] + 1000); // 1500

看到这样的形式,我们需要马上想到Proxy的解决方案。这题也有几个需要注意的特点:

  1. 首先是a[10]的形式,这很显然是对象属性读取操作,并且是通过[]中括号的方式进行的。
  2. 其次是a[10][20]的形式,这说明a[10]返回的也是对象,这样才能够链式的调用。如果a[10]返回的不是对象或者没有对象没有20属性,程序肯定是抛出错误的。
  3. 然后是隐式转换的问题,因为a[100][200] + 300需要运算出结果,那么对于a[100][200]来说,程序需要尝试对其进行数据的隐式转换。
  4. 最后是结果,我们需要将[]中括号传入的属性值都累加起来。显然需要一个累加器,用来保存最后返回的结果。

首先还是需要Proxy代理对象,让每一次对代理对象进行读取操作的时候,我们都要累加sum的值,因为最后我们要返回这个累计的结果。
为什么要在get函数最后返回receiver呢?因为receiver表示的是代理对象引用,所以我们返回代理对象就能够实现a[10][20]的调用方式。
为什么有key === Symbol.toPrimitive的判断呢?因为当程序执行到a[10][20] + 30的时候,程序会尝试将a[10][20]从代理对象的形式转为原始数据类型。当进行数据类型转换的时候,首先会去对象上寻找Symbol.toPrimitive的属性,也就是说明程序需要调用Symbol.toPrimitive方法将对象转为原始数据类型。所以我们需要返回一个函数用于Symbol.toPrimitive方法。
注意我们需要重置target对象中sum的值,否则会影响到后面的程序。

const a = new Proxy({
	// 累加器
	sum: 0
}, {
	get(target, key, receiver) {
		// 当key等于Symbol.toPrimitive的时候,说明a[10][20]需要从对象转换为原始数据类型
		if (key === Symbol.toPrimitive) {
			let { sum } = target;
			// 重置sum值
			target.sum = 0;
			// 返回Symbol.toPrimitive方法
			return (hint) => sum;
		} else {
			target.sum += Number(key);
		}
		return receiver;
	}
})

Symbol.toPrimitive()

对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:

  1. Number:该场合需要转为数值。
  2. String:该场合需要转为字符串。
  3. Default:该场合可以转为数值,也可以转为字符串。

为什么3 + obj会走default模式呢?因为+运算符的特殊性,因为它有可能是字符串拼接操作,也有可能是数值的加法运算。所以只能走default模式。

let obj = {
	[Symbol.toPrimitive](hint) {
		switch (hint) {
			case 'number':
				return 123;
			case 'string':
				return 'str';
			case 'default':
				return 'default';
			default:
				throw new Error();
		}
	}
};

2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HWOD:合并整型数组

一、知识点 合并整型数组目前有两种方法 合并数组并不一定需要真正的合并 1、下意识的方法 对两个整型数组分别排序,然后合并 2、不排序的方法 遍历两个数组,找出最小值,输出最小值。将两个数组中与最小值相等的位置置为超大值 重复以…

二叉树oj题(2)

1.二叉树的最近公共祖先 解题思路:方法一: 1.先判断p或者q 是不是 root当中的一个 2.左子树当中递归査找p或者q 3.右子树当中递归查找p或者q 如何查找: root 的 left 和 right 都不为空 ->root root的 left 为空 right 不为空->right这一侧找…

终于有人说明白了session、cookie和token的区别

一、首先介绍一下名词:Session、cookie、token,如下: 1.Session会话:客户端A访问服务器,服务器存储A的数据value,把key返回给客户端A,客户端A下次带着key(session ID)来…

ROS轻松入门(一)—— 基本概念:node节点、topic通信、service通信

node节点 ROS 中的每个节点都应该负责单一的、模块化的目的,例如控制车轮马达或发布来自激光测距仪的传感器数据。每个节点都可以通过主题、服务、操作或参数从其他节点发送和接收数据。 一个完整的机器人系统由许多协同工作的节点组成。在 ROS 2 中,单…

【java配置】jpcap的下载与idea配置

解决报错:Cannot resolve symbol ‘jpcap’ 1. jpcap的下载 官网下载链接 百度网盘下载 双击WinpPca安装,jacap1和jpcap2任选其中之一 2. idea配置 (1)查看当前使用jdk目录 File -> Project Settings -> SDKs &#…

STM32H750时钟频率和功耗以及RTC功能测试

STM32H750时钟频率和功耗和RTC功能测试 📌相关篇《STM32H750片外QSPI启动配置简要》 ✨在使用STM32CubeMX修改STM32H750时钟树参数时,如果使用软件自动求解,这是一个非常耗时的操作,有时候还不一定成功,还是推荐使用手…

2024成都直播电商硝烟再起,天府锋巢AI 时代拉开帷幕

在今年1月份的“AI重构电商”生态大会上,百度借力AI数字人直播和文心大模型能力杀入电商场内,强调“AI重塑电商”。成都兴隆湖畔,天府锋巢直播产业基地计划开展高质量、低成本、互动性更强的虚拟数字人直播,为直播行业注入新的活力…

低代码技术与仓储管理的新纪元:革命性的供应链变革

引言 在当今数字化时代,企业对于创新和效率的追求越发迫切。在这样的背景下,低代码技术应运而生,成为企业数字化转型的重要工具之一。低代码技术的崛起为企业提供了一种快速、灵活、成本效益高的开发方式,大大缩短了软件开发周期…

2024五一劳动节市集露营生活节活动策划方案

2024五一劳动节市集露营生活节(向野而生 躺平生活节主题)活动策划方案 方案页码:72页 文件格式:pptx 方案简介: 五一躺平生活节 咖啡一饮,书本一翻,轻松又自在,看着窗外的阳光,…

4.23日总结(项目总结)

1.项目: 今日项目通过一个在登录界面的一个静态变量,完成了区分老师和学生,能够分开老师和学生,并且不同身份的人进去会有不同的显示,以及登录链接主界面,还有学生和老师的不同的表,其次就是创…

「51媒体」新闻媒体邀约如何进行媒体宣传(方法)

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 新闻媒体邀约进行媒体宣传是一个策略性的过程,旨在吸引媒体的注意力并促使其对特定事件、产品发布或企业活动进行报道。以下是一些关键步骤和策略: 制定媒体传播方…

rust 学习笔记(13-19)

13 迭代器与闭包 Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是 函数式编程(functional programming)。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。 闭包(Closu…

网络爬虫快速入门及爬取百度搜索结果(附源码)

前言 爬虫的基本结构及工作流程 1. 确定目标 首先,确定你想要爬取的目标,包括目标网站或网页、需要提取的数据类型(如文本、图片、视频等)以及爬取的深度(单页、整个网站等)。 2. 获取网页内容 使用HT…

刷题之Leetcode242题(超级详细)

242.有效的字母异位词 力扣题目链接(opens new window)https://leetcode.cn/problems/valid-anagram/ 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2…

基于51单片机的数码管显示的proteus仿真

文章目录 一、数码管二、单个数码管显示0~F仿真图仿真程序 三、数码管静态显示74HC138译码器74HC245缓冲器仿真图仿真程序 四、数码管动态显示仿真图仿真程序 三、总结 一、数码管 数码管,也称作辉光管,是一种可以显示数字和其他信息的电子设备。它的基…

Abaqus2024 安装教程(附免费安装包资源)

鼠标右击软件压缩包,选择“解压到Abaqus2024”。 鼠标右击“此电脑”,选择“属性”。 点击“高级系统设置”。 点击“环境变量”。 点击“新建”。 变量名输入:NOLICENSECHECK 变量值输入:true 然后点击“确定”。 点击“确定”。…

SD-WAN多分支组网案例分享

随着企业规模持续扩大,业务版图日益多元,多分支组网已成为企业网络建设的核心议题。如何构建高效、安全且灵活的网络连接,成为企业急需解决的关键问题。近些年,SD-WAN技术的崭露头角,为企业带来了前所未有的解决方案。…

芯片数字后端设计入门书单推荐(可下载)

数字后端设计,作为数字集成电路设计的关键环节,承担着将逻辑设计转化为物理实现的重任。它不仅要求设计师具备深厚的电路理论知识,还需要对EDA工具有深入的理解和熟练的操作技能。尽管数字后端工作不像前端设计那样频繁涉及代码编写&#xff…

PLC无线通讯技术在汽车喷涂车间机械手臂上的应用

一、项目背景 在汽车生产装配工艺中,机械臂目前已经广泛地应用于装配、搬运等工业生产中,在机械臂系列产品中,汽车喷漆自动控制喷涂机械装置以其独特的优势,能够根据油漆喷涂量的大小,严格控制喷嘴与喷漆面之间距离等…

【数据库】聊聊普通索引和唯一索引怎么选

业务场景 在实际的业务中,一般都有用户信息表,而存储的数据包括(姓名、手机号、身份证号),对于业务层面来说一个人的身份证号是唯一确定的,所以在创建表的时候,针对身份证号列就可以选择创建普通索引或唯一索引。那么…
最新文章