代理模式 - Proxy

159 阅读2分钟

一、定义

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

二、核心

当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。

替身对象对请求做出一些处理之后, 再把请求转交给本体对象。

代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一些额外的事情。

三、实现

代理模式主要有三种:保护代理、虚拟代理、缓存代理

  • 保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子
// 主体,发送消息
function sendMsg (msg) {
  console.log(msg)
}

// 代理,对消息进行过滤
function proxySendMsg (msg) {
  if (typeof msg === 'undefined') {
    console.log('no msg find')
    return
  }

  sendMsg(msg)
}

// 测试使用
sendMsg('为了部落')  // => 为了部落
proxySendMsg();     // => no msg find

它的意图很明显,在访问主体之前进行控制,没有消息的时候直接在代理中返回了,拒绝访问主体,这数据保护代理的形式。

=> 有消息的时候对敏感字符进行了处理,这属于虚拟代理的模式。

  • 虚拟代理在控制对主体的访问时,加入了一些额外的操作。

例如,防抖、节流等...

// 函数防抖,频繁操作中不处理,直到操作完成之后(再过 delay 的时间)才一次性处理
function debounce(fn, delay) {
  delay = delay || 200;
  let timer = null;
  return function() {
    let arg = arguments;
      // 每次操作时,清除上次的定时器
      clearTimeout(timer);
      timer = null;
      // 定义新的定时器,一段时间后进行操作
      timer = setTimeout(function() {
        fn.apply(this, arg);
      }, delay);
  }
};
let count = 0;
// 主体
function scrollHandle() {
  console.log('---->count:', ++count); // 仅输出一次 ---->count: 1
}
// 代理
const proxyScrollHandle = (function() {
  return debounce(scrollHandle, 10);
})();
for(let i = 0; i < 10; i++) {
  proxyScrollHandle()
}
  • 缓存代理可以为一些开销大的运算结果提供暂时的缓存,提升效率
// 主体
function add() {
  var arg = [].slice.call(arguments);
  console.log('arguments:', arguments)
  return arg.reduce(function(a, b) {
      return a + b;
  });
}

// 代理
var proxyAdd = (function() {
  var cache = [];
  return function() {
      var arg = [].slice.call(arguments).join(',');
      
      // 如果有,则直接从缓存返回
      if (cache[arg]) {
          return cache[arg];
      } else {
          var ret = add.apply(this, arguments);
          cache[arg] = ret
          return ret;
      }
  };
})();

console.log(
  add(1, 2, 3, 4),
  add(1, 2, 3, 4),
  proxyAdd(10, 20, 30, 40),
  proxyAdd(10, 20, 30, 40)
); 

// 输出:
// arguments: [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
// arguments: [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
// arguments: [Arguments] { '0': 10, '1': 20, '2': 30, '3': 40 } // 仅一次,第二次使用了缓存
// 10 10 100 100

跳转:设计模式目录