梳理总是由易到难,代理模式又是不难理解的一种模式,因为做前端的小伙伴都是常用代理,那今天梳理一下所谓的代理模式
什么是代理模式
代理模式我们十分常见,不光说我们生活中所见的所谓的代理模式,经纪人,秘书,律师,包括投资经理,都属于是代理模式的体现,再看我们代码中实际体现的,host,服务器代理i请求,vue双向绑定的核心,VPN工具,这些都属于代理,那么代理身上有什么样的共性呢,那就是为一个对象找一个替代对象,以便对原对象进行访问
,至于为什么要有一个替代对象,我们从上述的例子出发来看,为什么有经纪人秘书投资精力,host.VPN工具这样的替代对象出现,简单来讲就是原对象不能完成某些功能,通过代理的拦截来替他完成,以便对象能正常的使用,有时候会觉得代理就是扩展一样,其实代理的核心我感觉是拦截
- 拦截
为什么说代理的核心是拦截,因为要替代原有对象做一些事情的时候,必须要拦截到原本对原有对象所做的事情, 或者说需要原有对象所做的事情,那拦截就是必不可少的了,有些形似代理模式的逻辑,并没有所谓的get和set拦截,只是给对象封装了一个方法,调用对象更改数据等操作的时候为了不改变原有对象,直接调用了封装的逻辑,这个封装的逻辑其实我感觉也算代理模式:举个栗子
例子如下,从思想出发来解释,缩减到极致的例子,并不代表实际运用是如此
//程序员吃饭
//餐厅
restaurant{
'西红柿鸡蛋盖饭':15,
'尖椒肉丝盖饭':18,
'红烧牛肉面':25
}
//现在程序员来吃饭了
restaurant['红烧牛肉面']
//然后得到价钱发现是25,然后购买
程序员直接到餐厅点餐拿到饭食,并没有经过任何中间途径,那这么看来就不是代理
//餐厅规则的添加,周六尖椒肉丝贵两块
funtion proxyRest(name){
let data = new Date().getDay()
if( data === 6 && name === '尖椒肉丝盖饭' ){
return restaurant[name]+2
} else{
return restaurant[name]
}
}
假设我们摒弃对象本身的代理来说,只从最原始的逻辑出发,那我们就多加了一个这样的方法,以后程序员再吃饭就不再是直接调用餐厅这个对象来获取,而是通过方法来知道要付多少钱,那这个方法就相当于一个代理,访问对象时,会经过代理一层,中间来做判断来做对象的访问,或者说改变一下,变成老板与店长更好理解
//老板经营餐厅
funtion Boss(){
this.restaurant{
'西红柿鸡蛋盖饭':15,
'尖椒肉丝盖饭':18,
'红烧牛肉面':25
}
return restaurant['红烧牛肉面']
}
//老板代理给店长管理店铺
funtion Boos(){
return shopowner(name)
}
funtion shopowner(name){
this.restaurant{
'西红柿鸡蛋盖饭':15,
'尖椒肉丝盖饭':18,
'红烧牛肉面':25
}
let data = new Date().getDay()
if( data === 6 && name === '尖椒肉丝盖饭' ){
return restaurant[name]+2
} else{
return restaurant[name]
}
}
想直接找老板买东西,老板说,想要啥, 我让店长去安排,店长就去进行判断进行返回,这就是工作被店长代理了
Proxy
有人会讲,这样是不是太过于麻烦,当然代理不是所有时刻都适合使用的,所有的设计模式都不是适用于所有的地方的,我们说会代理模式,有一个非常好用且完全遵循代理模式的东西就是Proxy,Proxy在vue3.0的梳理中已经讲过了,我们主要说Proxy对代理模式的使用
关于ES6的Proxy,概念是说:用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。很好理解,从语言基础层面去做出修改,为什么这样讲,我们来看
var objProxy = new Proxy(obj, {
get: function (target, propKey, receiver) {
.............
},
set: function (target, propKey, value, receiver) {
.............
}
});
get和set就是属性被取出使用和塞入更改的时候的拦截,es6语法中可以直接去重写这两个方法,来达到拦截的目的,这个也有讲过
- 验证代理
1.对数据做出验证
这个不做赘述了就,从上述的set方法就可以看出,我们在进行set操作的时候可以进行一系列的逻辑处理,那再set里进行逻辑处理就是验证代理
还有一个对应的get操作的拦截就是私有属性代理,就是在进行读取数据操作时,做逻辑判断,看是否允许读取数据
//借用网上表单验证代理的代码
const getValidateProxy = (target, validators) => {
return new Proxy(target, {
_validators: validators,
set(target, prop, value) {
if (value === '') {
console.error(`"${prop}" is not allowed to be empty`);
return target[prop] = false;
}
const validResult = this._validators[prop](value);
if(validResult.valid) {
return Reflect.set(target, prop, value);
} else {
console.error(`${validResult.error}`);
return target[prop] = false;
}
}
})
}
- 缓存代理
1.对方法做出代理
//cache看作缓存的储存起来的之前传入的参数和得出计算的数据的键值对
var objProxy = new Proxy(fn, {
apply(target, context, args) {
const argsString = args.join(' ');
if (cache.has(argsString)) {
// 如果有缓存,直接返回缓存数据
return cache.get(argsString);
}
const result = fn(...args);
//没有缓存的话储存进缓存中且返回计算结果
cache.set(argsString, result);
return result;
}
});
这样的好处就是,有些极其复杂的计算逻辑,就可以用缓存的方法来减少实际调用计算逻辑,要说能不能每次调用方法前都自己去调用一下缓存,然后在方法里每次计算也都加上这个逻辑,可以,就是不优雅,所以可以看出,所有的设计模式都是为了省事为了逻辑清晰