代理模式

128 阅读5分钟

一、介绍

定义:

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

个人理解:

有点类似与 房东和租房中介,明星和明星经纪人类似,与目标的操作全部被中间人拦截,经由中间人进行交互。

主要解决:

在直接访问对象时带来的问题:访问频繁,性能开销大,或者数据安全问题。

如何解决:

增加中间层,代理层。

关键代码:

实现与被代理类(对象)的组合。

应用实例:

  1. 虚拟代理中的图片预加载
  2. 事件代理中的多个子元素把时间委托给父元素
  3. vue3的响应式原理

使用场景:

众多,根据职责划分使用场景。

二、虚拟代理

延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。

你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。

实例:图片预加载

在网络不好的时候,图片加载较慢,这时候使用占位图,等图片完全加载完毕再渲染到页面上。优化用户体验。

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

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

var proxyImage = (function () {
  var img = new Image();
  img.onload = function () {
    // 代理图片的网络图片资源加载完毕
    // 设置真实展示图片的 src 属性
    myImage.setSrc(this.src);
  };
  return {
    setSrc: function (src) {
      myImage.setSrc("file:// /C:/Users/svenzeng/Desktop/loading.gif");
      img.src = src;
    },
  };
})();
proxyImage.setSrc("https://sponsors.vuejs.org/images/chrome_frameworks_fund.png");

三、缓存代理

缓存请求结果 (缓存代理)。 适用于需要缓存客户请求结果并对缓存生命周期进行管理时, 特别是当返回结果的体积非常大时。

代理可对重复请求所需的相同结果进行缓存, 还可使用请求参数作为索引缓存的键值。

实例

var mult = function(){
  console.log( '开始计算乘积' );
  var a = 1;
  for ( var i = 0, 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.apply( this, arguments );
  }
})();

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

四、事件代理

事件代理就是利用事件冒泡或事件捕获的机制把一系列的内层元素事件绑定到外层元素。

  • DOM事件流有3个阶段:捕获阶段,目标阶段,冒泡阶段;三个阶段的顺序为:捕获阶段——目标阶段——冒泡阶段;
  • 对于非目标阶段的元素,事件响应执行顺序遵循先捕获后冒泡的原则;通过暂缓执行捕获事件,可以达到先冒泡后捕获的效果;
  • 对于目标元素,事件响应执行顺序根据的事件的执行顺序执行;
  • 事件捕获是从顶层的Window逐层向内执行,事件冒泡则相反;
  • 事件委托(事件代理)是根据事件冒泡或事件捕获的机制来实现的。

拓展

代理和本体接口的一致性

  1. 用户可以放心地请求代理,他只关心是否能得到想要的结果。
  2. 在任何使用本体的地方都可以替换成使用代理。

其他代理模式

访问控制 (保护代理)

如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。

代理可仅在客户端凭据满足要求时将请求传递给服务对象。

本地执行远程服务 (远程代理)

适用于服务对象位于远程服务器上的情形。

在这种情形中, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。

记录日志请求 (日志记录代理)。

适用于当你需要保存对于服务对象的请求历史记录时。

代理可以在向服务传递请求前进行记录。

智能引用。

可在没有客户端使用某个重量级对象时立即销毁该对象。

代理会将所有获取了指向服务对象或其结果的客户端记录在案。 代理会时不时地遍历各个客户端, 检查它们是否仍在运行。 如果相应的客户端列表为空, 代理就会销毁该服务对象, 释放底层系统资源。

代理还可以记录客户端是否修改了服务对象。 其他客户端还可以复用未修改的对象。

  1. 事件代理:把一个元素响应事件(click、keydown......)的函数委托到另一个元素

  2. 保护代理:用于对象应该有不同访问权限的情况。

    • Proxy,它本身就是为拦截而生的,所以我们目前实现保护代理时,考虑的首要方案就是 ES6 中的 Proxy。
  3. 防火墙代理:控制网络资源的访问,保护主题不让“坏人”接近。

  4. 远程代理:为一个对象在不同的地址空间提供局部代表,在Java中,远程代理可以是另一个虚拟机中的对象。

  5. 智能引用代理:取代了简单的指针,它在访问对象时执行一些附加操作,比如计算一个对象被引用的次数。

  6. 写时复制代理:通常用于复制一个庞大对象的情况。写时复制代理延迟了复制的过程,当对象被真正修改时,才对它进行复制操作。写时复制代理是虚拟代理的一种变体,DLL(操作系统中的动态链接库)是其典型运用场景。