代理模式
代理模式为一个对象提供一个代用品或占位符,以便控制对它的访问。
代理模式的关键是,当客户不方便直接访问一个对象或者不满足需求时,提供一个替身对象来控制这个对象的访问,客户实际上访问的是替身对象,替身对象对请求做一些处理之后,把请求转发给本体对象。
原本的访问如下:
使用代理模式后访问如下:
保护代理和虚拟代理
代理模式案例
类似一个中介的作用 => 起到保护目标对象的作用(在执行具体功能之前,可以先进行其他操作)
例如:
茂茂今年18岁(年轻,脸白,活还好),但是苦于没有经济来源,这时候想到一个挣钱的好方法,兼职给客人洗脚,由于高超的手法,很讨富婆的喜欢,但是由于此时自身还是属于弱势群体,就需要一个代理人,此时精壮男子remi闪亮登场,来当这些弱势群体的代理人。
// 本体对象
function DaGongRen(name){ // 打工洗脚的人
this.name = name
}
DaGongRen.prototype = {
sfh:function (){
console.log(this.name + "手法好!洗过的富婆都说好!")
},
happyWithWomen:function (){
console.log(this.name + "很会逗富婆开心!")
},
happyWithMan: function (){ // 独门绝技
console.log(this.name + "很会与男人击剑!")
}
}
// 激活本体对象
var maomao = new DaGongRen("茂茂") // 打工人茂茂
var yaoyao = new DaGongRen("二摇") // 打工人二摇
// 代理人,代理人可以同时代理很多不同的本体对象,比如代理上面的茂茂和二摇
function ProxyRemi(user){ // 代理人remi
this.user = user
}
ProxyRemi.prototype = { // 代理人可以选择性拥有本体对象的功能,例如代理人remi知道他俩都有独门绝技击剑,但是没有选择帮忙代理,只是代理了其中两个功能
sfh:function (){
// 代理人还能进行一些功能的处理,例如需要先收小费,才会找打工人来洗脚
console.log("支付小费300块") // 支付小费
this.user.sfh() // 开始洗脚
},
happyWithWomen:function (){
this.user.happyWithWomen()
}
}
// 此时有一个叫北巷麋鹿(bxml)的富婆要来洗脚,指明需要活最好的来帮她洗,但是她并不知道这里谁的活最好,就需要通过找代理人remi了
// 激活代理人,new一下代理人
var bxml = new ProxyRemi(maomao) // 此时将头牌茂茂传入代理
console.log(bxml.sfh()) // 此时北巷麋鹿就可以享受"手法好"的服务了
var yy = new ProxyRemi(yaoyao)
console.log(yy.happyWithWomen())
代理缓存
记忆函数:用空间换时间,用一些数据结构保存计算的结果,有类似的计算,直接取已有的数据
// 代理缓存
// 主体
function add(a,b){
// 大量复杂的计算,需要大量的时间
return a + b
}
// 代理
function memorize(fn){ // 接收一个函数
var cache = {} // 数据结构容器,保存计算结果
return function (){ // 代理中返回的函数,可以设置接收的形参个数,也可以直接用arguments
// 创建一个独一无二的标志key,表示已经计算过的凭证
var key = Array.prototype.join.call(arguments,",")// 因为arguments没有join方法,call对象冒充,使得arguments对象拥有Array对象的join方法
if(key in cache){ // 如果已经计算过了,有数据
console.log("缓存")
return cache[key] // 数据结构的示例{"1,1":2,"1,2":3} key就是对象中对应的属性名
}else{ // 否则就是首次计算
console.log("计算")
return cache[key] = fn.apply(this,arguments) // 将计算的结果保存到缓存中
}
}
}
var memorizeAdd = memorize(add) // 调用代理,将主题函数传入代理,最后返回一个函数
// 下面都是操作代理
console.log(memorizeAdd(1,1)) // 计算得出2
console.log(memorizeAdd(1,1)) // 取缓存的2
console.log(memorizeAdd(1,2)) // 计算得出3
代理模式在vue3中的应用Proxy
以下是html代码:
<!--html代码-->
<div id = "app">
<h3 id = "paragraph"></h3>
<input type = "text" id = "input">
</div>
以下是js代码:
/* 先了解一下proxy的基本使用
// let proxy = new Proxy(代理的对象,拦截的操作)
let obj = {a:1} // 定义一个对象obj
// 生成proxy实例对象,并传入刚刚的obj对象,获取拦截操作
let proxy = new Proxy(obj,{
// 获取值拦截
get:function (target,name){ // 对象,属性名
console.log("拦截之后的获取值")
return target[name]
},
set:function (target,name,value){ // 对象,属性名,设置新的值
console.log("开始设置值")
target[name] = value
}
})
console.log(proxy.a) // 拦截值,会打印1
proxy.a = 2 // 将拦截的值重新设置
console.log(proxy.a) // 重新获取修改后的值,打印2
*/
// 正片开始
// 第一步,获取对象
let paragraph = document.getElementById("paragraph")
let input = document.getElementById("input")
// 第二步,准备好数据
let data = {
text:"今晚吃鸡!"
}
// 第三步,代理处理,将data对象传入并拦截处理
let proxy = new Proxy(data,{
// 获取值拦截
get:function (target,name){ // 对象,属性名
console.log("拦截之后的获取值")
return target[name]
},
// 设置值
set:function (target,name,value){ // 对象,属性名,设置新的值
console.log("开始设置值")
paragraph.innerHTML = value // 设置的时候给界面渲染
input.value = value
target[name] = value // 给对象上对应的属性重新赋值
}
})
// 触发set代码给界面渲染
proxy.text = data.text
// 文本框输入可以实时给模型赋值
input.addEventListener("input",function (e){ // 回调函数接收事件对象e
console.log(e,e.target)
proxy.text = e.target.value // 事件对象在文本框发生,所以e.target就是文本框对象,文本框对象输入的值实时发送给proxy
})
实现虚拟代理实现图片懒加载
在Web 开发中,图片预加载是一种常用的技术,如果直接给某个img 标签节点设置src 属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。
常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景就很适合使用虚拟代理
var myImage = (function () {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function () {
var img = new Image;
img.onload = function () {
myImage.setSrc(this.src);
};
return {
setSrc: function (src) {
myImage.setSrc('这里填写本地图片的加载路径');
img.src = src;
}
}
})();
proxyImage.setSrc('这里填写请求的网络图片路径');
代理的意义
单一职责原则:
就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。
面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏。
代理和本体接口的一致性
在客户看来,代理对象和本体是一致的, 代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别,这样做有两个好处:
1.用户可以放心地请求代理,他只关心是否能得到想要的结果。
2.在任何使用本体的地方都可以替换成使用代理。