代理模式 (Proxy Pattern)又称委托模式,它为目标对象创造了一个代理对象,以控制对目标对象的访问。
代理模式把代理对象插入到访问者和目标对象之间,从而为访问者对目标对象的访问引入一定的间接性。正是这种间接性,给了代理对象很多操作空间,比如在调用目标对象前和调用后进行一些预操作和后操作,从而实现新的功能或者扩展目标的功能。
明星总是有个助理,或者说经纪人,如果某导演来请这个明星演出,或者某个品牌来找明星做广告,需要经纪人帮明星做接洽工作。而且经纪人也起到过滤的作用,毕竟明星也不是什么电影和广告都会接。
打官司是件非常麻烦的事,包括查找法律条文、起草法律文书、法庭辩论、签署法律文件、申请法院执行等等流程。此时,当事人就可聘请代理律师来完成整个打官司的所有事务。当事人只需与代理律师签订全权委托协议,那么整个打官司的过程,当事人都可以不用出现。法院的一些复杂事务都可以通过代理律师来完成,而法院需要当事人完成某些工作的时候,比如出庭,代理律师才会通知当事人,并为当事人出谋划策。
在类似的场景中,有以下特点:
//目标对象target: 明星 const SuperStar = { name: ‘小鲜肉‘, scheduleFlag: false, scheduleFlagActually: false, playAdvertisement: function (advertisement) { console.log(this.name + ‘做任务: ‘ + advertisement) } } //代理对象proxy: 经纪人 const proxyAssistant = { name: ‘经纪人张某‘, scheduleTimeByPromise: function () { return new Promise((resolve, reject) => { console.log("schedule time") setTimeout(() => { console.log("小鲜肉空出时间了"); resolve(); }, 2000) }) }, scheduleTimeByProxy: function (advertisement) { const schedule = new Proxy(SuperStar, { // handler回调,prop为发生变化的属性,val为发生变化的值 // 有值发生变化后才执行回调,此处setTimeout先执行 // 只要对象属性发生变化就会监听执行回调 set(obj, prop, val) { console.log(‘proxy values: ‘, prop, val) if (prop !== ‘scheduleFlag‘) { return; } if (obj.scheduleFlag === false && val === true) { obj.scheduleFlag = true; obj.playAdvertisement(advertisement); } } }); setTimeout(() => { console.log("小鲜肉空出时间了"); schedule.scheduleFlag = true; }, 2000) }, scheduleTimeByDefineProperty: function (advertisement) { // 只能监听scheduleFlag变化,还要借用变量 Object.defineProperty(SuperStar, ‘scheduleFlag‘, { get() { return SuperStar.scheduleFlagActually }, set(val) { if (SuperStar.scheduleFlagActually === false && val === true) { SuperStar.scheduleFlagActually = true; SuperStar.playAdvertisement(advertisement) } } }); setTimeout(() => { console.log("小鲜肉空出时间了"); SuperStar.scheduleFlag = true; }, 2000) }, playAdvertisement: function (condition, advertisement) { if (condition > 100000) { console.log(‘接受任务: ‘ + advertisement); //SuperStar.playAdvertisement(advertisement); // proxyAssistant.scheduleTimeByPromise() // .then(()=> { // SuperStar.playAdvertisement(advertisement) // }); proxyAssistant.scheduleTimeByProxy(advertisement); } else { console.log(‘不满足条件,不做‘) } } } //访问者visitor proxyAssistant.playAdvertisement(100, "拍广告"); proxyAssistant.playAdvertisement(1000000, "拍广告");
拦截器的思想在实战中应用非常多,比如我们在项目中经常使用 Axios 的实例来进行 HTTP 的请求,使用拦截器 interceptor
可以提前对 request
请求和 response
返回进行一些预处理,比如:
request
请求头的设置,和 Cookie 信息的设置;Date
类型的数据在请求前进行一些格式约定好的序列化操作;response
的一些通用报错处理,比如使用 Message 控件抛出错误;除了 HTTP 相关的拦截器之外,还有 vue-router、react-router 路由跳转的拦截器,可以进行一些路由跳转的预处理等操作。
拦截器看起来似乎和装饰者模式很像,但是要注意装饰者模式和代理模式的区别,代理模式控制访问者对目标对象的访问,而装饰者模式只给目标对象添加功能,原有功能不变且可直接使用。Axios 拦截器是可以取消请求的,vue-router 路由拦截器也可以进行路由截停和重定向等等复杂操作,这些场景下,无疑是代理模式,因为这里的拦截器控制了对目标对象的访问,如果没有进行访问控制而只进行消息预处理和后处理,那么则可以当作是装饰者模式。
现在的很多前端框架或者状态管理框架都使用上面介绍的 Object.defineProperty
和 Proxy
来实现数据的响应式化,比如 Vue、Mobx、AvalonJS 等,Vue 2.x 与 AvalonJS 使用前者,而 Vue 3.x 与 Mobx 5.x 使用后者。
Vue 2.x 中通过 Object.defineProperty
来劫持各个属性的 setter/getter
,在数据变动时,通过发布-订阅模式发布消息给订阅者,触发相应的监听回调,从而实现数据的响应式化,也就是数据到视图的双向绑定。
为什么 Vue 2.x 到 3.x 要从 Object.defineProperty
改用 Proxy
呢,是因为前者的一些局限性,导致的以下缺陷:
vm.items[indexOfItem] = newValue
;vm.items.length = newLength
;Set
、WeakSet
、Map
、WeakMap
的变化;Class
类型的数据;除此之外还有性能上的差异,基于这些原因,Vue 3.x 改用 Proxy
来实现数据监听了。当然缺点就是对 IE 用户的不友好,兼容性敏感的场景需要做一些取舍。
反向代理对应的是正向代理(Forward Proxy),他们的区别是:
反向代理一般在处理跨域请求的时候比较常用,属于服务端开发人员的日常操作了,另外在缓存服务器、负载均衡服务器等等场景也是使用到代理模式的思想。
原文:https://www.cnblogs.com/Nyan-Workflow-FC/p/13039653.html