模式代理

2022/9/17 设计模式

# 什么是代理模式

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

比如,明星都有经纪人作为代理。如果想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演 出的细节和报酬都谈好之后,再把合同交给明星签。 javascriptproxynginx , webpack, vite 等的代理。

# 实现一个代理模式

# 图片预加载

在加载图片的时候,如果直接给图片一个 src 属性,如果图片太大或者网络不好的情况下就会出现一块空白区域,我们可以先用一张 loading 图片占位,然后异步加载图片,等图片加载完毕在把他填充到 imgsrc 中去。

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

myImage.setSrc('//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png');

使用一层代理

var proxyImage = (function () {
  var img = new Image();
  img.onload = function () {
    myImage.setSrc(this.src);
  };
  return {
    setSrc: function (src) {
      myImage.setSrc(
        'https://tiebapic.baidu.com/forum/w%3D120%3Bh%3D120/sign=31dc183a627f9e2f7035190a2f0b8119/0df431adcbef7609db6133436bdda3cc7dd99e80.jpg?tbpicau=2022-09-19-05_08b14b6d7860ca07829e8c1078db3e4c'
      );
      img.src = src;
    },
  };
})();

我们通过 proxyImage 间接地访问 myImageproxyImage 控制了客户对 myImage 的访问,并且在此过程中加入一些额外的操作,在真正的图片加载好之前,先把 img 节点的 src 设置为 一张本地的 loading 图片。

这里的代理就是对 myImage 添加了一层行为控制(未加载完图片之前,加载一张 loading 图)。如果有一天你不在需要这个代理,那么直接访问本体对象就可以。

这里用到了虚拟代理,虚拟代理就是把一些开销很大的对象,延迟到真正需要它的时候才去创建,这也是虚拟代理在惰性加载中的使用。

# 合并请求

比如说一个实时性要求比较高的系统来说,需要频繁的去服务器获取数据,这个时候就可以使用一层代理合并请求,有点节流的味道了,

const proxyHttp = (function () {
  const cache = [];
  // 保存一段时间内需要同步的 ID timer; // 定时器
  return function (id) {
    cache.push(id);
    if (timer) {
      // 保证不会覆盖已经启动的定时器 return; }
      timer = setTimeout(function () {
        http(cache.join(','));
        clearTimeout(timer); // 清空定时器
        timer = null;
        cache.length = 0; // 清空 ID 集合
      }, 2000);
    }
  };
})();

# 缓存代理

// 乘积
const mult = function () {
  console.log('mult star');
  let res = 1;
  for (let i = 0, l = arguments.length; i < l; i++) {
    res = res * arguments[i];
  }
  return res;
};
mult(2, 3);
// mult star
// 6
mult(2, 3, 4);
// mult star
// 24

// 加入缓存代理函数:
const 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);
proxyMult(1, 2, 3, 4);
// mult star
// 24
// 24

第二次调用的时候并没有重新结算,而是使用了之前已经计算的值。通过增加缓存代理的方式,mult 函数可以继续专注于自身的职责——计算乘积,缓存的功能 是由代理对象实现的。

缓存代理可以用于 ajax 分页请求,缓存上一页的数据。

# 动态创建代理

先实现一个加法

// 加法
var plus = function () {
  var res = 0;
  for (var i = 0, l = arguments.length; i < l; i++) {
    res = res + arguments[i];
  }
  return res;
};

通过传入高阶函数的方式,可以为各种计算方法创建缓存代理。这些计算方法被当作参数传入一个专门用于创建缓存代理的工厂中, 这样一来,我们就可以为乘法、加法创建缓存代理。

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));
  };
};

var proxyMult = createProxyFactory(mult);
var proxyPlus = createProxyFactory(plus);

# 总结

在什么情况下使用代理呢?

  1. 在开销比较大的情况下做一些惰性加载,使用的时候在去加载
  2. 当不方便直接访问某个对象的时候,使用代理
  3. 需要做一些缓存的时候,使用代理

JavaScript 常用的是虚拟代理缓存代理

Last Updated: 2026/06/09/ 06:29:26