proxy详细介绍与使用
proxy详细介绍与使用
proxy 对象用于创建一个对象的代理,是在目标对象之前架设一个拦截,外界对该对象的访问,都必须先通过这个拦截。通过这种机制,就可以对外界的访问进行过滤和改写。
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler);
target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
const obj = {};
const proxy=new Proxy(obj,{get:function(target,propKey){return 1}
})console.log(proxy.name) // 1
console.log(proxy.value) // 1
在上面代码中,Proxy接受两个参数,第一个是要代理的目标对象(上面的是空对象obj),在没有设置Proxy的情况下,对proxy的操作就是访问obj。第二个参数是配置对象,对每个被代理的操作,需要提供一个对应的处理函数,这个函数用于拦截对应的操作。
上面代码中,配置对象有个get方法,用来拦截对目标对象属性的访问请求。get方法中的两个参数分别是目标对象和所要访问的属性。由于设置的是返回1,所以访问任何属性都得到1。
如果handler没有设置拦截,就等同于直接通向原对象。
const obj = {};
const proxy=new Proxy(obj, {})
proxy.a = 1
console.log(obj.a) // 1
console.log(obj === proxy) // false
handler是空对象,没有拦截作用,访问proxy等同于访问obj。但是代理对象和原对象并不相等。
handler配置
handler 对象是一个容纳一批特定属性的占位符对象。它包含有 proxy 的各个捕获器。
1. get
get方法用于拦截某个属性的读取操作,接受三个参数,(目标对象,属性名,Proxy或者proxy实例本身),第三个参数可选。
const obj={value:1
}
const proxy=new Proxy(obj,{get:function(target,propKey){if(propKey in target){return target[propKey]}else{throw new ReferenceError(`${propKey} 不存在`)}}
})
console.log(proxy.value) // 1
proxy.name //报错
如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
get方法可以被继承
const obj = {a: 1
};
let proto = new Proxy(obj, {get(target, propertyKey) {return target[propertyKey];}
});
let newObj = Object.create(proto);
newObj.a // "GET foo"
拦截操作定义在原型对象上面,所以如果读取obj对象继承的属性时,拦截会生效。
下面是一个get方法的第三个参数的例子,它总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。
const proxy = new Proxy({}, {get: function(target, key, receiver) {return receiver;}
});
proxy.getReceiver === proxy // true
对于
get方法,proxy存在一些约束
- 如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同,否则会抛出异常。
const obj = {}; Object.defineProperty(obj, "a", {configurable: false,enumerable: false,value: 1,writable: false }); const proxy=new Proxy(obj,{get:function(target,propKey){return 2 // return 1 就没问题} }); proxy.a- 如果要访问的目标属性没有配置访问方法,即
get方法是undefined的,则返回值必须为undefined。const obj = {}; Object.defineProperty(obj, "a", {configurable: false,get: undefined }); const proxy=new Proxy(obj,{get:function(target,propKey){return 1; // return undefined 就没问题} }); proxy.a
2. set
用来拦截某个属性的赋值操作,接受四个参数(目标对象、属性名、属性值、最初被调用的对象(通常是proxy实例本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身))),第四个参数可选。
const proxy = new Proxy({}, {set: function(obj,prop,value){if(prop === 'value') {if(!Number.isInteger(value)){throw new TypeError('不是整数')}if(value>10){throw new RangeError('太大了')}}obj[prop] = value}
})
proxy.value = 5
proxy.value = 'val' //报错
proxy.value = 20 //报错
由于设置了存值函数set,任何不符合要求的value属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。
来看看set在原型链上的情况.
const proxy = new Proxy({}, {set: function(obj, prop, value, receiver) {console.log(receiver === newObj); // trueif(prop === 'value') {if(!Number.isInteger(value)){throw new TypeError('不是整数')}if(value>10){throw new RangeError('太大了')}}obj[prop] = value}
})
let newObj = Object.create(proxy);
newObj.value = 1;
将代理对象作为原型创建一个新对象,此时第四个参数就是这个新对象而不是proxy实例。
对于
set方法,proxy也存在一些约束
- 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。
const obj = {}; Object.defineProperty(obj, "a", {configurable: false,enumerable: false,value: 1,writable: false }); const proxy=new Proxy(obj,{set: function(obj, prop, value) {obj[prop] = value} }); proxy.a = 1; proxy.a // 还是 undefined- 如果目标属性没有配置存储方法,即
set的是undefined,则不能设置它的值.const obj = {}; Object.defineProperty(obj, "a", {configurable: false,set: undefined }); const proxy=new Proxy(obj,{set: function(obj, prop, value) {obj[prop] = value} }); proxy.a = 1; proxy.a // 还是 undefined- 在严格模式下,如果
set方法返回false,那么也会抛出一个异常。
3. apply
拦截函数的调用,接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
var target = function () { return 'target'; };
var handler = {apply: function () {return 'proxy';}
};var p = new Proxy(target, handler);
p()
// "proxy"
变量p是 Proxy 的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串。
4. has
用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。接受两个参数,分别是目标对象、需查询的属性名。
var handler = {has (target, key) {return key in target;}
};
var target = { prop: 'foo' };
var proxy = new Proxy(target, handler);
'propa' in proxy // false
如果原对象不可配置或者禁止扩展,这时has拦截会报错。
var obj = { a: 10 };
Object.preventExtensions(obj);var p = new Proxy(obj, {has: function(target, prop) {return false;}
});
'a' in p // TypeError is thrown
has方法拦截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判断一个属性是对象自身的属性,还是继承的属性。可以看作是针对 in 操作的钩子。
5. construct
用于拦截new命令
var handler = {construct (target, args, newTarget) {return new target(...args);}
};
接受三个参数,目标对象,构造函数的参数对象,new命令作用的构造函数。
construct方法返回的必须是一个对象,否则会报错。
6. deletePrototype
拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
var handler = {deleteProperty (target, key) {invariant(key, 'delete');delete target[key];return true;}
};
function invariant (key, action) {if (key[0] === '_') {throw new Error(`Invalid attempt to ${action} private "${key}" property`);}
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property
deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。
目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错。
7. defineProperty
拦截了Object.defineProperty操作。
var handler = {defineProperty (target, key, descriptor) {return false;}
};
var proxy = new Proxy({}, handler);
proxy.a = 'a' // 不会生效
8. getOwnPropertyDescriptor
拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。
var handler = {getOwnPropertyDescriptor (target, key) {return Object.getOwnPropertyDescriptor(target, key);}
};
var target = {a: 'a' };
var proxy = new Proxy(target, handler);
Object.getOwnPropertyDescriptor(proxy, 'a')
// { value: 'a', writable: true, enumerable: true, configurable: true }
9. getPrototypeOf
用来拦截获取对象原型。
var proto = {};
var p = new Proxy({}, {getPrototypeOf(target) {return proto;}
});
Object.getPrototypeOf(p) === proto // true
10. isExtensible
拦截Object.isExtensible操作。
var p = new Proxy({}, {isExtensible: function(target) {console.log("called");return true;}
});
Object.isExtensible(p)
// "called"
// true
11. ownKeys
拦截对象自身属性的读取操作。
let target = {a: 1,b: 2,c: 3
};
let handler = {ownKeys(target) {return ['a'];}
};
let proxy = new Proxy(target, handler);
Object.keys(proxy)
// [ 'a' ]
12. preventExtensions
拦截Object.preventExtensions()。
var proxy = new Proxy({}, {preventExtensions: function(target) {return true;}
});
Object.preventExtensions(proxy)
// Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible
13. setPrototypeOf
用来拦截Object.setPrototypeOf方法。
var handler = {setPrototypeOf (target, proto) {throw new Error('Changing the prototype is forbidden');}
};
var proto = {};
var target = function () {};
var proxy = new Proxy(target, handler);
Object.setPrototypeOf(proxy, proto);
// Error: Changing the prototype is forbidden
只要修改target的原型对象,就会报错。
proxy比Object.defineProperty具有的优势
了解Proxy的基本用法过后接下来我们再深入探讨一下相比于Object.defineProperty, Proxy到底有哪些优势。
首先最明显的优势就是在于Proxy要更为强大一些,那这个强大具体体现在Object.defineProperty只能监听到对象属性的读取或者是写入,而Proxy除读写外还可以监听对象中属性的删除,对对象当中方法的调用等等。
这里我们为obj对象定义一个Proxy对象,在Proxy对象的处理对象中的外的添加一个deleteProperty的代理方法,这个方法会在外部对当前这个代理对象进行delete操作时会自动执行。
这个方法同样接收两个参数,分别是代理目标对象和所要删除的这个属性的名称。
const obj = {a: 1
}
const proxy = new Proxy(obj, {deleteProperty(target, propKey) {delete target[propKey]}
})delete proxy.a
vue2.x的响应式并不具备这样的处理,而是需要一个$delete方法来解决。
export default {data() {return {obj: {a: 1}}},methods: {deleteA() {delete this.obj.a; // a属性确实删除了,但是没有触发对应的更新// this.$delete(this.obj, 'a') 能删除a,并触发更新}}
}
比如上方的使用delete直接删除a,并不会触发更新,这是因为Object.defineProperty只能监听到对象属性的读取或者是写入,没办法监听删除。我们顺便来看看vue2.x的解决办法:
function del(target: any[] | object, key: any) {// ...if (isArray(target) && isValidArrayIndex(key)) {target.splice(key, 1)return}const ob = (target as any).__ob__// ...delete target[key]if (!ob) {return}// 获取对象的观察者__ob__,并手动调用更新if (__DEV__) {ob.dep.notify({type: TriggerOpTypes.DELETE,target: target,key})} else {ob.dep.notify()}
}
可以看到 vue 实例的 $delete 处理了一些边界情况,处理了数组和对象的删除,最重要的是,获取了变量上的ob指针指向的观察者实例,并通知依赖dep里的watcher更新。
第二点优势就是对于数组对象进行监视,
通常我们想要监视数组的变化,基本要依靠重写数组方法,这也是vue2.x的实现方式,proxy可以直接监视数组的变化。以往我们想要通过Object.defineProperty去监视数组的操作最常见的方式是重写数组的操作方法,大体的方式就是通过自定义的方法去覆盖掉数组原型对象上的push,shift之类的方法,以此来劫持对应的方法调用的过程。
这里来看如何直接使用Proxy对象来对数组进行监视。这里我们定义一个list数组,然后对这个list数组进行Proxy监视。
在这个Proxy对象的处理对象上我们去添加一个set方法,用于监视数据的写入,在这个方法的内部我们打印参数的值,然后再target对象上设置传入的值,最后返回一个true表示写入成功。
这样我们再外部对数组的写入都会被监视到,例如我们这里通过push向数组中添加值。
const arr = []
const proxy = new Proxy(arr, {set(target, propKey, value) {target[propkey] = value;return true}
})
Proxy内部会自动根据push操作推断出来他所处的下标,每次添加或者设置都会定位到对应的下标property。
最后相比于Object.defineProperty还有一点优势就是,Proxy是以非入侵的方式监管了对象的读写,那也就是说一个已经定义好的对象我们不需要对对象本身去做任何的操作,就可以监视到他内部成员的读写,而defineProperty的方式就要求我们必须按特定的方式单独去定义对象当中那些被监视的属性。
相关文章:
proxy详细介绍与使用
proxy详细介绍与使用 proxy 对象用于创建一个对象的代理,是在目标对象之前架设一个拦截,外界对该对象的访问,都必须先通过这个拦截。通过这种机制,就可以对外界的访问进行过滤和改写。 ES6 原生提供 Proxy 构造函数,…...
基于YOLOv5的舰船检测与识别系统(Python+清新界面+数据集)
摘要:基于YOLOv5的舰船检测与识别系统用于识别包括渔船、游轮等多种海上船只类型,检测船舰目标并进行识别计数,以提供海洋船只的自动化监测和管理。本文详细介绍船舰类型识别系统,在介绍算法原理的同时,给出Python的实…...
【C#】List数据去重
系列文章 【C#】单号生成器(定义编号规则、流水号、产生业务单号) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/129129787 【C#】日期范围生成(构建本周开始、结束日期) 本文链接:https…...
避免踩坑,教给你VSCode中最常用到的6项功能
这里为程序员介绍VSCode中包含的许多令人兴奋的Tips。 1. 插件市场中免费下载使用CodeGeeX插件 AI辅助编程工具CodeGeeX,是完全免费,开源开放给所有开发者使用。程序员普遍反应使用这个插件后,代码编写效率提升2倍以上。 CodeGeeX插件拥有…...
ThingsBoard开源物联网平台智慧农业实例快速部署教程(Ubuntu、CentOS适用)
ThingsBoard部署教程文档 文章目录ThingsBoard部署教程文档1. JDK环境安装2. 安装thingsBoard2.1 ThingsBoard软件包安装2.2 PostgreSQL安装2.3 PostgreSQL初始化配置3. 修改ThingsBord的配置4. 运行安装脚本测试5. 访问测试6. 导入一个仪表盘库6.1 导出仪表盘并导入自己的项目…...
【Java Spring基本问题】记录面试题宝典中自己不熟悉的Spring问题
文章目录Spring Bean定义装配Spring Bean生命周期Spring Bean容器Spring 循环依赖Spring 事务Autowired和ResourceSpring Bean定义装配 参考文章 1. 定义Spring Bean的三种方式 XML文件定义Spring Bean JavaConfig定义Spring Bean Component注解定义SpringBean 2. 装配Spri…...
I2C协议简介 Verilog实现
I2C协议 IIC 协议是三种最常用的串行通信协议(I2C,SPI,UART)之一,接口包含 SDA(串行数据线)和 SCL(串行时钟线),均为双向端口。I2C 仅使用两根信号线…...
服务器被DDoS攻击,怎么破?
文章目录前言网站受到DDoS的症状判断是否被攻击查看网络带宽占用查看网络连接TCP连接攻击SYN洪水攻击防御措施TCP/IP内核参数优化iptables 防火墙预防防止同步包洪水(Sync Flood)Ping洪水攻击(Ping of Death)控制单个IP的最大并发…...
实现完全二叉树
文章目录1、树概念及结构2、孩子兄弟表示法3、二叉树3.1、二叉树的概念3.2、特殊的二叉树3.3、二叉树的存储4、堆的性质5、数组结构实现完全二叉树1、结构体的定义2、初始化堆3、销毁堆4、交换函数5、向上调整函数6、插入数据7、向下调整函数8、删除堆顶数据函数9、判断是否空堆…...
【独家】华为OD机试 - 矩阵最值(C 语言解题)
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本期题目:矩阵最值 题目 给定一个仅包…...
C++模板(进阶)
文章目录非类型模板参数类模板的特化类模板的概念函数模板特化类模板的特化全特化偏特化参数的进一步限制模板的分离编译模板的优缺点非类型模板参数 模板参数分类型形参与非类型形参. 类型形参: 出现在模板参数列表中,跟在class,typename之类的参数类型名称. 非类型形参: 就是…...
【数据分析之道(二)】列表
文章目录专栏导读1、列表介绍2、访问列表中的值3、列表增加和修改4、删除元素5、列表函数6、列表方法专栏导读 ✍ 作者简介:i阿极,CSDN Python领域新星创作者,专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》,本专栏针…...
架构师必须要掌握的大小端问题
一、什么是大端和小端 所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 简单来说:大端——高尾端,小端——低尾端 举个例子,比如数字 0x12 34 56 78…...
2023年ACM竞赛班 2023.3.20题解
目录 瞎编乱造第一题 瞎编乱造第二题 瞎编乱造第三题 瞎编乱造第四题 瞎编乱造第五题 不是很想编了但还是得编的第六题 不是很想编了但还是得编的第七题 还差三道题就编完了的第八题 还差两道题就编完了的第九题 太好啦终于编完了 为啥一周六天早八阿 瞎编乱造第一题…...
什么是语法糖?Java中有哪些语法糖?
本文从 Java 编译原理角度,深入字节码及 class 文件,抽丝剥茧,了解 Java 中的语法糖原理及用法,帮助大家在学会如何使用 Java 语法糖的同时,了解这些语法糖背后的原理1 语法糖语法糖(Syntactic Sugar&#…...
STM32学习(五)
GPIO General Purpose Input Output,通用输入输出端口,简称GPIO。 作用: 采集外部器件的信息(输入)控制外部器件的工作(输出) GPIO特点 1,不同型号,IO口数量可能不一样…...
STM32的CAN总线调试经验分享
相关文章 CAN总线简易入门教程 CAN总线显性电平和隐性电平详解 STM32的CAN总线调试经验分享 文章目录相关文章背景CAN总线CAN控制器CAN收发器调试过程硬件排查CAN分析仪芯片CAN控制器调试总结背景 最近负责的一个项目用的主控芯片是STM32F407IGT6,需要和几个电机控…...
深度剖析自定义类型(结构体、枚举、联合)——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容是心心念念的结构体啦,其实在此之前,我也写过结构体的知识点,只是并没有很深入,那么,今天我会仔细来学习自定义类型的知识点,下面…...
《水经注地图服务》发布的全球影像数据在水经微图中调用
(本文首发于“水经注GIS”公号,订阅“水经注GIS”公号,为你分享更多GIS技术 )1、引言古人云:“工欲善其事,必先利其器。”意思是说:工匠想要使他的工作做好,一定要先让工具锋利&…...
MyBatis --- 缓存、逆向工程、分页插件
一、MyBatis的缓存 1.1、MyBatis的一级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问 使一级缓存失效的四种情况: 1、…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
