JavaScript 设计模式之代理模式

作者: tww844475003 分类: 前端开发 发布时间: 2021-06-08 21:50

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

代理模式是一种有用的模式,在生活中可以找到很多代理模式的场景。
1、保护代理之不直接面对,不愿暴露,而是通过代理方式与外部接触;
2、虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才创建。
  • 虚拟代理实现图片预加载

在 web 开发中,图片预加载是一种常用的技术,如果直接给某个 img 标签节点设置 src 属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。常见的做法是先用一张 loading 图片占位,然后用异步方式加载图片,等图片加载好了再把它填充到 img 节点里,这种场景就很适合使用虚拟代理。

var showImg = (function() {
  var imgNode = document.createElement('img');

  document.body.appendChild(imgNode);

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

var proxyImg = (function() {
  var img = new Image;
  
  img.onload = function() {
    showImage.setSrc(this.src);
  }

  return {
    setSrc: function(src) {
      showImg.setSrc('loading.gif');

      img.src = src;
    }
  }
})();

proxyImg.setSrc('thumb.png');
  • 虚拟代理合并 http 请求

比如一个文件同步功能,当用户选中一个 checkbox 就代表该文件要同步到另外一台服务器上面

<input type="checkbox" id="vue.js"> vue.js
<input type="checkbox" id="react.js"> react.js
<input type="checkbox" id="angular.js"> angular.js
<input type="checkbox" id="node.js"> node.js
<input type="checkbox" id="require.js"> require.js
<input type="checkbox" id="sea.js"> sea.js
<script>
var syncFiles = function(id) {
  console.log('开始同步文件,id 为:' + id);
}

var checkbox = document.getElementsByTagName('input');
for (var i = 0, c; c = checkbox[i++];) {
  c.onclick = function() {
    if (this.checked === true) {
      syncFiles(this.id)
    }
  }
}
</script>

如此这样点击就直接发送同步请求,频繁的网络请求将会带来相当大的开销。如果可以通过一个代理函数 proxySyncFiles 来收集一段时间之内的请求,最后一次性发送给服务器,这样就能大大减轻服务器的压力。

<input type="checkbox" id="vue.js"> vue.js
<input type="checkbox" id="react.js"> react.js
<input type="checkbox" id="angular.js"> angular.js
<input type="checkbox" id="node.js"> node.js
<input type="checkbox" id="require.js"> require.js
<input type="checkbox" id="sea.js"> sea.js
<script>
var syncFiles = function(id) {
  console.log('开始同步文件,id 为:' + id);
}

var proxySyncFiles = (function() {
  var cache = [],
      timer;

  return function(id) {
    cache.push(id);
    if (timer) {
      return;
    }

    timer = setTimeout(function() {
      syncFiles(cache.join(','));
      clearTimeout(timer);
      timer = null;
      cache.length = 0;
    }, 2000)
  }
})();

var checkbox = document.getElementsByTagName('input');
for (var i = 0, c; c = checkbox[i++];) {
  c.onclick = function() {
    if (this.checked === true) {
      proxySyncFiles (this.id)
    }
  }
}
</script>
  • 缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前的一致,则可以直接返回前面存储的运算结果。

var mult = function() {
  console.log('开始计算');

  var a = 1;
  for (var i = 0, len = arguments.length; i < len; 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);
  }
})();

console.log(proxyMult(1, 2, 3, 4)); // 24
console.log(proxyMult(1, 2, 3, 4)); // 24
  • 用高阶函数动态创建代理

通过传入高阶函数这种更加灵活的方式,可以为种计算方法创建缓存代理。

var mult = function() {
  console.log('开始计算');

  var a = 1;
  for (var i = 0, len = arguments.length; i < len; i++) {
    a = a * arguments[i];
  }

  return a;
}

var plus = function() {
  console.log('开始计算');

  var a = 0;
  for (var i = 0, len = arguments.length; i < len; 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);
  }
};

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

console.log(proxyMult(1, 2, 3, 4)); // 24
console.log(proxyMult(1, 2, 3, 4)); // 24
console.log(proxyPlus(1, 2, 3, 4)); // 10
console.log(proxyPlus(1, 2, 3, 4)); // 10
前端开发那点事
微信公众号搜索“前端开发那点事”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注