yliu

时来天地皆同力,运去英雄不自由

代理模式


前言

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

下面就介绍最常用的几种场景

保护代理

例如作为孩子我们总希望他健康成长,在成长路上遇到的烦恼会被家长解决,这样来看其实就是保护代理,下面用一段伪代码来实现。

function Children() {
  this.knowledge = [];
}
Children.prototype.Study = function(content) {
  // ...学习,增加知识
  this.knowledge.push(content);
};
var Parent = (function() {
  var small = new Children();
  return {
    knowledge: function(content) {
      if (content === "bad") {
        // 坏的,过滤掉
      } else {
        small.Study(content);
      }
    }
  };
})();

上面用伪代码的形式来实现了一个保护例子,不过你可能会困惑,这样不是多此一举么,我们完全可以在Children内部实现,不过这样做

  1. 增加了程序耦合度,如果想要过滤更多,或者某一天不过滤了,还要更改代码
  2. 违背了单一原则,对于代码的阅读和维护,造成了更多的成本

缓存代理

缓存代理是很实用的一个例子,例如一个阶乘函数,计算 n 的阶乘,最多需要保存 n 个调用记录,我们可能这样写

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}
factorial(5); // 120

不过如果重复计算 5 或者 10,很明显造成了多次浪费,这个时候就可以使用代理,把结果缓存下来,如果存在就直接返回。

function next(n, total) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}
var factorial = (function() {
  var obj = {};
  return function(v) {
    if (obj[v]) {
      return obj[v];
    }
    obj[v] = next(v);
    return obj[v];
  };
})();
factorial(5); // 120
factorial(5); // 120

上面就将结果缓存了下来,除此之外还可以引用在异步请求中可以节省加载的时间。

应用场景

上面简单介绍了两种模式,实际上还有很多,比如虚拟代理、远程代理、防火墙代理等,不过这里不做介绍了,下面看看使用场景。

jquery

jquery基于链式调用,比如$('.a').html('').text(''),不过你会不会很好奇,$()调用的时候它是怎么记录这个值的呢,每次调用$()存储的 dom 都不相同,实际上它就是借用了代理模式,这里直接贴代码了

function Jquery(dom) {
  this.dom = dom;
}
// 代理者
function $(dom) {
  return new Jquery(dom);
}
$.prototype = Jquery.prototype = {
  // 只做演示,不涉及具体代码
  html() {
    return this;
  },
  text() {
    return this;
  }
};
$(".a")
  .html("")
  .text("");

vue

我们在使用 vue 的时候给定了配置项data,它是一个对象,里面的属性会被 vue 响应式处理,我们可以通过this.的方式来简单访问,也是用了代理模式,下面就用一段伪 vue 代码来说明

class Mvvm {
  constructor(option = {}) {
    this.$option = option;
    this._data = option.data || {};
    // 代理数据
    for (const name in this._data) {
      Object.defineProperty(this, name, {
        configurable: true,
        enumerable: true,
        get() {
          return this._data[name];
        },
        set(v) {
          return (this._data[name] = v);
        }
      });
    }
  }
}
export default Mvvm;

可以复制上面代码,自己简单试下。