文章目录
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问
1、简单代理模式的例子
小明给A送花的例子
不使用代理:
js
var Flower = function() {};
var xiaoming = {
sendFlower: function(target) {
var flower = new Flower();
target.receiveFlower(flower);
}
}
var A = {
receiveFlower: function(flower) {
console.log('收到花' + flower);
}
}
xiaoming.sendFlower(A);
引入代理B,通过B来个A 送花:
js
var Flower = function () { };
var xiaoming = {
sendFlower: function (target) {
var flower = new Flower();
target.receiveFlower(flower);
}
}
// 代理B
var B = {
receiveFlower: function (flower) {
A.receiveFlower(flower)
}
}
var A = {
receiveFlower: function (flower) {
console.log('A收到花' + flower);
}
}
xiaoming.sendFlower(B);
2、保护代理和虚拟代理
2.1 保护代理
代理 B 可以帮助 A过滤掉一些请求,比如送花的人中年龄太大的或者没有宝马的,这种请求就可以直接在代理 B处被拒绝掉。这种代理叫作保护代理
2.1 虚拟代理
虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建
代码如下:
js
var B = {
receiveFlower: function (flower) {
A.listenGoodMood(function () { // 监听 A 的好心情
var flower = new Flower(); // 延迟创建 flower 对象
A.receiveFlower(flower);
});
}
};
3、虚拟代理实现图片预加载
Web 开发中,图片预加载是一种常用的技术,如果直接给某个 img 标签节点设置 src 属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。常见的做法是先用一张loading 图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到 img 节点里,这种场景就很适合使用虚拟代理
创建一个 img 标签,并且提供一个对外的 setSrc 接口:
js
var myImage = (function () {
var imageNode = document.createElement('img');
document.body.appendChild(imageNode);
return {
setSrc: function (src) {
imageNode.src = src;
}
}
})()
myImage.setSrc('https://img0.baidu.com/it/u=1371113496,4161662645&fm=26&fmt=auto');
问题:
在网速较差时,图片被加载好之前,页面中有一段长长的空白时间
解决:
现在开始引入代理对象 proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中
将出现一张占位的菊花图 loading.gif,来提示用户图片正在加载
代码如下:
js
var myImage = (function () {
var imageNode = document.createElement('img');
document.body.appendChild(imageNode);
return {
setSrc: function (src) {
imageNode.src = src;
}
}
})()
var proxyImage = (function () {
var img = new Image();
img.onload = function () {
myImage.setSrc(this.src);
}
return {
setSrc: function(src) {
myImage.setSrc('./assets/loading.gif');
img.src = src;
}
}
})()
proxyImage.setSrc('https://img0.baidu.com/it/u=1371113496,4161662645&fm=26&fmt=auto');
4、代理和本体接口的一致性
代理对象和本体都对外提供了 setSrc 方法,说明代理对象和本体是一致的, 代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别,这样做有两个好处:
1、用户可以放心地请求代理,他只关心是否能得到想要的结果
2、在任何使用本体的地方都可以替换成使用代理
5、缓存代理
缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果
缓存代理例子------计算乘积
创建一个用于求乘积的函数
js
var mult = function () {
var a = 1;
for (var i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return a;
};
加入缓存代理函数:
js
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));
};
})();
测试:
js
var res1 = proxyMult(2, 3, 6);
var res2 = proxyMult(2, 3, 6);
console.log(res1); // 24
console.log(res2); // 24 本体 mult 函数并没有被计算,proxyMult 直接返回了之前缓存好的计算结果
通过增加缓存代理的方式,mult 函数可以继续专注于自身的职责------计算乘积,缓存的功能是由代理对象实现的
6、用高阶函数动态创建代理
通过传入高阶函数这种更加灵活的方式,可以为各种计算方法创建缓存代理
现在这些计算方法被当作参数传入一个专门用于创建缓存代理的工厂中, 这样一来,我们就可以为乘法、加法、减法等创建缓存代理,代码如下:
js
/**************** 计算乘积 *****************/
var mult = function () {
var a = 1;
for (var i = 0; i < arguments.length; i++) {
a = a * arguments[i];
}
return a;
};
/**************** 计算和 *****************/
var plus = function () {
var a = 0;
for (var i = 0; i < arguments.length; i++) {
a = a + arguments[i];
}
return a;
};
/**************** 创建缓存代理的工厂 *****************/
var createProxyFactory = function (fn) {
var cache = {};
return function () {
var args = Array.prototype.join.call(arguments, ',');
if (args in cache) {
return cache[args];
}
return (cache[args] = fn.apply(this, arguments));
};
};
测试:
js
var proxyMult = createProxyFactory(mult);
console.log(proxyMult(2, 3, 6));
console.log(proxyMult(2, 3, 6));
var proxyPlus = createProxyFactory(plus);
console.log(proxyPlus(2, 3, 6));
console.log(proxyPlus(2, 3, 6));
7、小结
代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理。虽然代理
模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟