代理模式

138 阅读4分钟

1. 简单代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。

  • 例子1 :小明追女神

var Flower = function () { };

var xiaoming = {
  sendFlower: function (targer) {
    var flower = new Flower();
    targer.receiveFlower(flower);
  }
};

var A = {
  receiveFlower: function (flower) {
    console.log("收到花",flower);
  }
};

xiaoming.sendFlower("A");

//引入代理模式

var Flower = function () { };

var xiaoming = {
  sendFlower: function (targer) {
    var flower = new Flower();
    targer.receiveFlower(flower);
  }
};
var B = {
  sendFlower: function (flower) {
    A.receiveFlower(flower);
  }
};

var A = {
  receiveFlower: function (flower) {
    console.log("收到花",flower);
  }
}

xiaoming.sendFlower(B);

2. 保护代理、虚拟代理

保护代理:代理B可以帮助A过滤掉一些请求,比如送花的人中年龄太大的或者没有宝马的, 这种请求就可以直接在代理B处被拒绝掉。这种代理叫作保护代理

虚拟代理: 假设现实中的花价格不菲,导致在程序世界里,new Flower 也是一个代价昂贵的操作,那么我们可以把new Flower的操作交给代理B去执行,代理B会选择在A心情好时再执行 newFlower

var B = {
  receiveFlower: function( flower ){
    A.listenGoodMood(function(){ // 监听 A 的好心情
      var flower = new Flower(); // 延迟创建 flower 对象
      A.receiveFlower( flower );
    });
  }
};

3. 虚拟代理实现图片预加载

  • 在 Web 开发中,图片预加载是一种常用的技术,如果直接给某个 img 标签节点设置 src 属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到 img 节点里,这种场景就很适合使用虚拟代理。
/* 1.首先创建一个普通的本体对象,这个对象负责往页面中创建一个 img 标签,
并且提供一个对外的 setSrc 接口,外界调用这个接口,便可以给该 img 标签设置 src 属性: */

var myImage = (function () {
  var imgNode = document.createElement('img');
  document.body.appendChild(imgNode);

  return {
    setSrc: function (src) {
      imgNode.src = src;
    }
  }
})();

/* 我们把网速调至 5KB/s,然后通过 MyImage.setSrc 给该 img 节点设置 src,可以看到,在图片
被加载好之前,页面中有一段长长的空白时间。 */
myImage.setSrc('http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg');

/* 2.引入代理对象 proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中
将出现一张占位的菊花图 loading.gif, 来提示用户图片正在加载。*/

var proxyImage = (function () {
  var img = new Image;
  img.onload = function () {
    myImage.setSrc(this.src);
  }
  return {
    setSrc: function (src) {
      myImage.setSrc('file:// /C:/Users/svenzeng/Desktop/loading.gif');
      img.src = src;
    }
  })();


  /* 现在我们通过 proxyImage 间接地访问 MyImage。proxyImage 控制了客户对 MyImage 的访问,并
且在此过程中加入一些额外的操作,比如在真正的图片加载好之前,先把 img 节点的 src 设置为
一张本地的 loading 图片。 */
 proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

3. 代理的意义

// 不用代理的预加载图片函数
var MyImage = (function () {
  var imgNode = document.createElement('img');
  document.body.appendChild('imgNode');
  var img = new Image;

  img.onload = function () {
    imgNode.src = img.src;
  }
  return {
    setSrc: function (src) {
      imgNode.src = 'file:// /C:/Users/svenzeng/Desktop/loading.gif'
      img.src = src;
    }
  }
})();

MyImage.setSrc('http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg');
  • 上段代码中的 MyImage 对象除了负责给 img 节点设置 src外,还要负责预加载图片。 不符合面向对象设计的原则 —— 单一职责原则,也违反开放—封闭原则
  • 意义:如果添加代理对象,我们需要的只是给 img 节点设置 src,预加载图片只是一个锦上添花的功能。如果能把这个操作放在另一个对象里面,自然是一个非常好的方法。于是代理的作用在这里就体现出来了,代理负责预加载图片,预加载的操作完成之后,把请求重新交给本体 MyImage。

4. 缓存代理实现

  • 缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果。
// 先创建一个用于求乘积的函数:
var mult = function () {
  console.log('开始计算乘积');
  var a = 1;
  for (var i = 1, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }
  return a;
};

mult(2, 3); // 输出:6
mult(2, 3, 4); // 输出:24

//现在加入缓存代理函数:
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(this, arguments));
  };
})();

proxyMult(1, 2, 3, 4); // 输出:24
proxyMult(1, 2, 3, 4); // 输出:24

//当我们第二次调用 proxyMult( 1, 2, 3, 4 )的时候,本体 mult 函数并没有被计算,proxyMult
// 直接返回了之前缓存好的计算结果。

5. 用高阶函数动态创建代理

  • 通过传入高阶函数这种更加灵活的方式,可以为各种计算方法创建缓存代理。现在这些计算方法被当作参数传入一个专门用于创建缓存代理的工厂中, 这样一来,我们就可以为乘法、加法、减法等创建缓存代理,代码如下:
/**************** 计算乘积 *****************/
var mult = function () {
  var a = 1;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }
  return a;
};

/**************** 计算加和 *****************/
var plus = function () {
  var a = 0;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a + arguments[i];
  }
  return a;
};

/**************** 创建缓存代理的工厂 *****************/

var createProxyFaxtory = 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));
  };
};

var proxyMult = createProxyFactory(mult),
    proxyPlus = createProxyFactory(plus);
alert(proxyMult(1, 2, 3, 4)); // 输出:24
alert(proxyMult(1, 2, 3, 4)); // 输出:24
alert(proxyPlus(1, 2, 3, 4)); // 输出:10
alert(proxyPlus(1, 2, 3, 4)); // 输出:10