一 说说对代理模式的理解?
1.1 是什么
代理模式(Proxy Pattern)是为⼀个对象提供⼀个代⽤品或占位符,以便控制对它的访问
代理模式的关键是,当客⼾不⽅便直接访问⼀个对象或者不满⾜需要时,提供⼀个替⾝对象来控制这个对象的访问,客⼾实际上访问的是替⾝对象

在⽣活中,代理模式的场景是⼗分常⻅的,例如我们现在如果有租房、买房的需求,更多的是去找链家等房屋中介机构,⽽不是直接寻找想卖房或出租房的⼈谈。此时,链家起到的作⽤就是代理的作⽤
1.2 使用
在 ES6 中,存在 proxy 构建函数能够让我们轻松使⽤代理模式:
const proxy = new Proxy(target, handler);
⽽按照功能来划分, javascript 代理模式常⽤的有:
1.2.1 缓存代理
缓存代理可以为⼀些开销⼤的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前⼀致,则可以直接返回前⾯存储的运算结果
如实现⼀个求积乘的函数,如下:
var muti = function() {
console.log("开始计算乘积");
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
现在加载缓存代理,如下:
var proxyMult = (function() {
var cache = {};
return function() {
var args = Array.prototype.join.call(arguments, ",");
if (args in cache) {
return cache[args];
}
return (cache[args] = mult.apply(this, arguments));
};
})();
proxyMult(1, 2, 3, 4); // 输出:24
proxyMult(1, 2, 3, 4); // 输出:24
当第⼆次调⽤ proxyMult(1, 2, 3, 4) 时,本体 mult 函数并没有被计算, proxyMult 直接返回了之前缓存好的计算结果
1.2.2 虚拟代理
虚拟代理把⼀些开销很⼤的对象,延迟到真正需要它的时候才去创建
常⻅的就是图⽚预加载功能:
未使⽤代理模式如下:
let MyImage = (function() {
let imgNode = document.createElement('img');
document.body.appendChild(imgNode);
// 创建⼀个Image对象,⽤于加载需要设置的图⽚
let img = new Image;
img.onload = function() {
// 监听到图⽚加载完成后,设置src为加载完成后的图⽚
imgNode.src = img.src;
};
return {
setSrc: function(src) {
// 设置图⽚的时候,设置为默认的loading图
imgNode.src =
ttps: //img.zcool.cn/community/01deed576019060000018c1bd2352d.gif';
// 把真正需要设置的图⽚传给Image对象的src属性
img.src = src;
}
}
})();
MyImage.setSrc('https://xxx.jpg');
MyImage 对象除了负责给 img 节点设置 src 外,还要负责预加载图⽚,违反了⾯向对象设计的原则-单⼀职责原则
上述过程 loding 则是耦合进 MyImage 对象⾥的,如果以后某个时候,我们不需要预加载显⽰loading这个功能了,就只能在 MyImage 对象⾥⾯改动代码
使⽤代理模式,代码则如下:
// 图⽚本地对象,负责往⻚⾯中创建⼀个img标签,并且提供⼀个对外的setSrc接⼝
let myImage = (function() {
let imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
//setSrc接⼝,外界调⽤这个接⼝,便可以给该img标签设置src属性
setSrc: function(src) {
imgNode.src = src;
}
}
})();
// 代理对象,负责图⽚预加载功能
let proxyImage = (function() {
// 创建⼀个Image对象,⽤于加载需要设置的图⽚
let img = new Image;
img.onload = function() {
// 监听到图⽚加载完成后,给被代理的图⽚本地对象设置src为加载完成后的图⽚
myImage.setSrc(this.src);
}
return {
setSrc: function(src) {
// 设置图⽚时,在图⽚未被真正加载好时,以这张图作为loading,提⽰⽤⼾图⽚正在加载
myImage.setSrc(
'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif');
img.src = src;
}
}
})();
proxyImage.setSrc('https://xxx.jpg');
使⽤代理模式后,图⽚本地对象负责往⻚⾯中创建⼀个 img 标签,并且提供⼀个对外的 setSrc 接⼝
代理对象负责在图⽚未加载完成之前,引⼊预加载的 loading 图,负责了图⽚预加载的功能
上述并没有改变或者增加 MyImage 的接⼝,但是通过代理对象,实际上给系统添加了新的⾏为
并且上述代理模式可以发现,代理和本体接⼝的⼀致性,如果有⼀天不需要预加载,那么就不需要代理对象,可以选择直接请求本体。其中关键是代理对象和本体都对外提供了 setSrc ⽅法
1.3 应用场景
现在的很多前端框架或者状态管理框架都使⽤代理模式,⽤与监听变量的变化
使⽤代理模式代理对象的访问的⽅式,⼀般⼜被称为拦截器,⽐如我们在项⽬中经常使⽤ Axios 的实例来进⾏ HTTP 的请求,使⽤拦截器 interceptor 可以提前对 请求前的数据 服务器返回的数据进⾏⼀些预处理
以及上述应⽤到的缓存代理和虚拟代理