网站首页 > 技术文章 正文
番外话:周末为了放松大家的压力和提升大家的学习体验,我分享了自己发现的一些前端学习视频,大家可以和我一起在家里看看视频轻松学习,本周视频资源链接:
什么是代理模式
在现实生活中,proxy是一个被授权代表其他人的人。比如,许多州允许代理投票,这意味着你可以授权他人在选举中代表你投票。
拿最常遇到的收快递这一个社会行为举例吧。很早之前,我们收发快递都是直接和快递员交互的,例如:
而现在,加入了代理之后,可以通过第三方替我们接收快递,这是生活当中一个非常常见的例子,可以说代理的存在,大大的便利了“我”这个对象。即:
你很可能听说过proxy服务器,它会接收来自你这的所有流量,代表你发送给另一端,并把响应返回给你。当你不希望请求的目的地知道你请求的具体来源时,使用proxy服务器就很有用了。所有的目标服务器看到的只是来自proxy服务器的请求。
再接近本文的主题一些,这种类型的代理和ES6 proxy要做的就很类似了,涉及到使用类(B)去包装类(A)并拦截/控制对(A)的访问。
当你想进行以下操作时proxy模式通常会很有用:
- 拦截或控制对某个对象的访问
- 通过隐藏事务或辅助逻辑来减小方法/类的复杂性
- 防止在未经验证/准备的情况下执行重度依赖资源的操作
当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少内存用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。
上面是维基百科中对代理模式的一个整体的定义.而在JavaScript中代理模式的具体表现形式就是ES6中的新增对象---Proxy
ES6中的代理模式
ES6所提供Proxy构造函数能够让我们轻松的使用代理模式:
let proxy = new Proxy(target, handler);
Proxy构造函数传入两个参数,第一个参数target表示所要代理的对象,第二个参数handler也是一个对象用来设置对所代理的对象的行为。如果想知道Proxy的具体使用方法,可参考阮一峰的《 ECMAScript入门 - Proxy 》。
Proxy构造器可以在全局对象上访问到。通过它,你可以有效的拦截针对对象的各种操作,收集访问的信息,并按你的意愿返回任何值。从这个角度来说,proxy和中间件有很多相似之处。
具体来说,proxy允许你拦截许多对象上常用的方法和属性,最常见的有get,set,apply(针对函数)和construct(针对使用new关键字调用的构造函数)。关于使用proxy可以拦截的方法的完整列表,请参考规范。Proxy还可以配置成随时停止接受请求,有效的取消所有针对被代理的目标对象的访问。这可以通过一个revoke方法实现。
代理模式常用场景
1 剥离验证逻辑
一个把Proxy用于验证的例子,验证一个数据源中的所有属性都是同一类型。下面的例子中我们要确保每次给numericDataStore数据源设置一个属性时,它的值必须是数字。
这很有意思,但有多大的可能性你会创建一个这样的对象呢?肯定不会。。。
如果你想为一个对象上的部分或全部属性编写自定义的校验规则,代码可能会更复杂一些,但我非常喜欢Proxy可以帮你把校验代码与核心代码分离开这一点。难道只有我讨厌把校验代码和方法或类混在一起吗?
通过这种方式,你就可以无限的扩展校验规则而不用修改类或方法。
再说一个和校验有关的点子。假设你想检查传给一个方法的参数并在传入的参数与函数签名不符时输出一些有用的帮助信息。你可以通过Proxy实现此功能,而不用修改该方法的代码。
在看一个表单验证的例子。Proxy构造函数第二个参数中的set方法,可以很方便的验证向一个对象的传值。我们以一个传统的登陆表单举例,该表单对象有两个属性,分别是account和password,每个属性值都有一个简单和其属性名对应的验证方法,验证规则如下:
下面我们来使用Proxy实现一个通用的表单验证器
我们调用getValidateProxy方法去生成了一个代理对象userFormProxy,该对象在设置属性的时候会根据validators的验证规则对值进行校验。这我们使用的是console.error抛出错误信息,当然我们也可以加入对DOM的事件来实现页面中的校验提示。
2 真正的私有属性
在JavaScript中常见的做法是在属性名之前或之后放一个下划线来标识该属性仅供内部使用。但这并不能阻止其他人读取或修改它。
在下面的例子中,有一个我们想在api对象内部访问的apiKey变量,但我们并不想该变量可以在对象外部访问到。
通过使用ES6 Proxy,你可以通过若干方式来实现真实,完全的私有属性。
首先,你可以使用一个proxy来截获针对某个属性的请求并作出限制或是直接返回undefined。
你还可以使用hastrap来掩盖这个属性的存在。
3 默默的记录对象访问
针对那些重度依赖资源,执行缓慢或是频繁使用的方法或接口,你可能喜欢统计它们的使用或是性能。Proxy可以很容易的悄悄在后台做到这一点。
注意:你不能仅仅使用applytrap来拦截方法。任何使用当你要执行某个方法时,你首先需要get这个方法。因此,如果你要拦截一个方法调用,你需要先拦截对该方法的get操作,然后拦截apply操作。
这很酷,因为你可以记录各种各样的信息而不用修改应用程序的代码或是阻塞代码执行。并且只需要在这些代码的基础上稍事修改就可以记录特性函数的执行性能了。
4 给出提示信息或是阻止特定操作
假设你想阻止其他人删除noDelete属性,想让调用oldMethod方法的人知道该方法已经被废弃,或是想阻止其他人修改doNotChange属性。以下是一种快捷的方法。
5 防止不必要的资源消耗操作--缓存代理
假设你有一个服务器接口返回一个巨大的文件。当前一个请求还在处理中,或是文件正在被下载,又或是文件已经被下载之后你不想该接口被再次请求。代理在这种情况下可以很好的缓冲对服务器的访问并在可能的时候读取缓存,而不是按照用户的要求频繁请求服务器。缓存代理可以将一些开销很大的方法的运算结果进行缓存,再次调用该函数时,若参数一致,则可以直接返回缓存中的结果,而不用再重新进行运算。例如在采用后端分页的表格时,每次页码改变时需要重新请求后端数据,我们可以将页码和对应结果进行缓存,当请求同一页时就不用在进行ajax请求而是直接返回缓存中的数据。在这里我会跳过大部分代码,但下面的例子还是足够向你展示它的工作方式。
再列举一个比较好理解的例子:下面我们以没有经过任何优化的计算斐波那契数列的函数来假设为开销很大的方法,这种递归调用在计算40以上的斐波那契项时就能明显的感到延迟感。
现在我们来写一个创建缓存代理的工厂函数:
当我们第二次调用getFibProxy(40)时,getFib函数并没有被调用,而是直接从cache中返回了之前被缓存好的计算结果。通过加入缓存代理的方式,getFib只需要专注于自己计算斐波那契数列的职责,缓存的功能使由Proxy对象实现的。这实现了我们之前提到的单一职责原则。
6. 即时撤销对敏感数据的访问
Proxy支持随时撤销对目标对象的访问。当你想彻底封锁对某些数据或API的访问时(比如,出于安全,认证,性能等原因),这可能会很有用。以下是一个使用revocable方法的简单例子。注意当你使用它时,你不需要对Proxy方法使用new关键字。
到此,我们列举了很多实用的代理模式案例,大家应该对这个设计模式有了一定的印象和理解,希望大家能在接下来的工作中继续实践和积累,也期待大家在评论区和我分享自己的理解和实践过程
总结
在面向对象的编程中,代理模式的合理使用能够很好的体现下面两条原则:
- 单一职责原则: 面向对象设计中鼓励将不同的职责分布到细粒度的对象中,Proxy 在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念。
- 开放-封闭原则:代理可以随时从程序中去掉,而不用对其他部分的代码进行修改,在实际场景中,随着版本的迭代可能会有多种原因不再需要代理,那么就可以容易的将代理对象换成原对象的调用
对于代理模式 Proxy 的作用主要体现在三个方面:
1、 拦截和监视外部对对象的访问
2、 降低函数或类的复杂度
3、 在复杂操作前对操作进行校验或对所需资源进行管理
下期预告:
JavaScript设计模式之观察者模式(Observer Pattern)
参考
- 详解ES6中的代理模式——Proxy
- https://juejin.im/post/5b71a90bf265da282958701b
猜你喜欢
- 2025-06-13 Linux 上利用Nginx代理uWSGI处理Flask web应用
- 2025-06-13 如何隐藏代理器服务地址?企业级IP匿名化与反追踪技术
- 2025-06-13 宝塔面板使用Nginx反向代理解决跨域问题
- 2025-06-13 海尔集团武汉中心总经理孙梁君——以智慧家电 升级品质生活
- 2025-06-13 给小白的 Nginx 10分钟入门指南(nginx入门教程)
- 2025-06-13 反向代理以及其使用场景(反向代理啥意思)
- 2025-06-13 Vue炼金术:解锁前端开发的进阶之道
- 2025-06-13 93.8k Star 的内网穿透神器 frp:DIY开发者必备的反向代理
- 2025-06-13 Nginx正向代理、反向代理、负载均衡及性能优化
- 2025-06-13 深入理解跨域及常见误区揭秘(深入理解跨域及常见误区揭秘论文)
你 发表评论:
欢迎- 523℃Oracle分析函数之Lag和Lead()使用
- 519℃几个Oracle空值处理函数 oracle处理null值的函数
- 517℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 504℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 501℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 493℃【数据统计分析】详解Oracle分组函数之CUBE
- 472℃Oracle有哪些常见的函数? oracle中常用的函数
- 471℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端富文本编辑器 (47)
- 前端路由 (61)
- 前端数组 (73)
- 前端排序 (47)
- 前端定时器 (47)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)