Skip to content

代理模式(Proxy)

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

使用场景:

  • 图片预加载
  • 合并请求
  • 懒加载
  • 缓存代理、防火墙代理、远程代理、保护代理、智能引用代理、写时复制代理等等

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。

简单说就是,代理代替客户去访问本体

代理分为两种:

  1. 保护代理:用于控制不同权限的对象对目标对象的访问。
  2. 虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建。

举例:小K喜欢一个女孩小U,打算送一束花给小U,刚好他打听到他们两个之间有一个共同的好友小D,然后内向的小K决定让小D代替他来完成这件事。

当然肯定失败收场,怂是泡不了女孩的,哈哈哈!

javascript
const Flower = function() {}

const k = {
	sendFlower: function(target) {
  	const flower = new Flower();
    target.receiveFlower(flower);
  }
}

const d = {
	receiveFlower: function(flower) {
  	u.receiveFlower(flower);
  }
}

const u = {
	receiveFlower: function(flower) {
  	console.log('收到花啦' + flower);
  }
}

k.sendFlower(d);

这是一个最简单的代理模式代码,实际上这个例子不适合使用代理,因为自己送和代理送没有本质的区别,但这里只是为了演示。


当小U好心情的时候再去送花,但小K只认识小U几天,无法判断什么时候她才是好心情,而好友小D知道。这时就比较时候使用代理模式啦。

修改 du

javascript
const d = {
	receiveFlower: function(flower) {
    u.listenGoodMood(function () {
    	u.receiveFlower(flower);
    })
  }
}

const u = {
	receiveFlower: function(flower) {
  	console.log('收到花啦' + flower);
  },
  // 一秒后好心情
  listenGoodMood: function(fn) {
  	setTimeout(function() {
    	fn();
    }, 1000);
  }
}

注意:代理和本体接口需一致(即实现相同的接口,在任何使用本体的地方都可以替换成使用代理)

不要与策略模式弄混,策略模式是算法部分委托给算法的实现函数(策略函数);而代理是代替客户访问本体,代理来控制访问的时间或者其他的操作,如权限,或者延时等。

案例

一、虚拟代理实现预加载

在图片还没有加载完成之前,页面上先展示 load 的 GIF 图片,等图片加载好后替换上去。

javascript
const myImage = (function() {
	const imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  
  return {
  	setSrc: function(src) {
    	imgNode.src = src;
    }
  }
})();

const proxyImage = (function() {
  const img = new Image();
  img.onload = function() {
  	myImage.setSrc(this.src);
  }
  
  return {
  	setSrc: function(src) {
      myImage.setSrc('file:// /C:load.gif');
      img.src = src;
    }
  }
})();

proxyImage.setSrc('http://zhu.jpg');

二、虚拟代理合并HTTP请求

主要思路是:本体实现请求,而代理实现把一段时间内请求缓存起来(可以利用闭包),定时执行一遍,实现减少HTTP请求。适用于频繁进行网络请求的地方,如同步等。

代码略。

三、缓存代理

主要思路是:本体实现需求,而代理把需求的结果缓存起来(使用闭包),当下次有相同条件的需求时,直接返回,不再去调用本体。

javascript
const mult = function () {
  let a = 1;
  for (let i = 0, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }

  return a;
}

const sum = function () {
  let a = 0;
  for (let i = 0, l = arguments.length; i < l; i++) {
    a = a + arguments[i];
  }

  return a;
}

// 缓存代理工厂
const createProxyFactory = function (fn) {
  const cache = {};

  return function () {
    const args = Array.prototype.join.call(arguments, ',');
    if (args in cache) {
      return cache[args];
    }
    return cache[args] = fn.apply(this, arguments);
  }
}

const proxyMult = createProxyFactory(mult),
  proxySum = createProxyFactory(sum);
console.log(proxyMult(1, 2, 3, 4)); // 24
console.log(proxySum(1, 2, 3, 4)); // 10

四、虚拟代理在惰性加载中的应用

主要思路是:代理实现与本体一致的接口,当没有触发惰性加载条件时,处理由代理来完成。当触发惰性加载后,代理再去调用本体的方法,把之前的缓存交给本体实现。

代码略。

Released under the MIT License.