代理最简单的实现

  1. var Flower = function () { };
  2. var xiaoming = {
  3. sendFlower (target) {
  4. var flower = new Flower();
  5. target.receiveFlower(flower)
  6. }
  7. }
  8. // 代理对象
  9. var B = {
  10. receiveFlower (flower) {
  11. // 代理对象帮目标对象过滤一些请求--保护代理
  12. A.listenGoodMood(A.receiveFlower.bind(null, flower))
  13. }
  14. }
  15. // 目标对象
  16. var A = {
  17. receiveFlower (flower) {
  18. console.warn('收到了', flower)
  19. },
  20. listenGoodMood (fn) {
  21. setTimeout(fn, 1200)
  22. }
  23. }
  24. xiaoming.sendFlower(B);

保护代理和虚拟代理

保护代理

代理对象帮目标对象过滤一些请求.用于控制不同权限的对象对目标对象的访问

  1. var B = {
  2. receiveFlower (flower) {
  3. // 代理对象帮目标对象过滤一些请求--保护代理
  4. A.listenGoodMood(A.receiveFlower.bind(null, flower))
  5. }
  6. }

虚拟代理

虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建.

  1. var B = {
  2. receiveFlower (flower) {
  3. A.listenGoodMood(() => {
  4. let flower = new Flower();
  5. A.receiveFlower(flower)
  6. })
  7. }
  8. }

保护代理用于控制不同权限的对象对目标对象的访问,但在JavaScript并不容易实现保护代理,因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式,本章主要讨论的也是虚拟代理。

虚拟代理实现图片预加载

  1. var myImage = (function () {
  2. var image = document.createElement('img');
  3. document.body.appendChild(image);
  4. return {
  5. setSrc (src) {
  6. image.setAttribute('src', src);
  7. }
  8. }
  9. })();
  10. var proxyDefaultMyImage = (function () {
  11. var image = new Image();
  12. image.onload = function (event) {
  13. // 网络请求的开销大,为了用户体验,回调中更新UI
  14. myImage.setSrc(this.src);
  15. }
  16. return {
  17. setSrc (src) {
  18. myImage.setSrc('../images/Snipaste_2020-10-25_21-14-55.png');
  19. image.src = src;
  20. }
  21. }
  22. })();
  23. proxyDefaultMyImage.setSrc('https://placekitten.com/220/220')

代理和本体接口的一致性

上一节说到,如果有一天我们不再需要预加载,那么就不再需要代理对象,可以选择直接请求本体。其中关键是代理对象和本体都对外提供了setSrc方法,在客户看来,代理对象和本体是一致的,代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别,这样做有两个好处。

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

虚拟代理合并HTTP请求

  1. var uploadFile = function (id) {
  2. console.warn('需要上传的id是:' + id);
  3. }
  4. var proxyUploadFile = (function () {
  5. var cache = [];
  6. var timer = null;
  7. return function (id) {
  8. cache.push(id);
  9. if (timer) {
  10. return;
  11. }
  12. timer = setTimeout(() => {
  13. uploadFile(cache);
  14. clearTimeout(timer);
  15. timer = null;
  16. cache.length = 0;
  17. }, 2000);
  18. }
  19. })();
  20. for (i = 1; i <= 10; i++) {
  21. document.querySelector('#a' + i).addEventListener('change', e => {
  22. if (e.target.checked) {
  23. proxyUploadFile(e.target.id)
  24. }
  25. })
  26. }

缓存代理

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

  1. var mult = function () {
  2. var result = 1;
  3. for (var i = 0; i < arguments.length; i++) {
  4. result *= arguments[i]
  5. }
  6. return result;
  7. }
  8. var proxyMult = (function () {
  9. var cache = {};
  10. return function () {
  11. var arr = [].slice.apply(arguments);
  12. var key = arr.join();
  13. if (cache[key]) {
  14. return cache[key];
  15. } else {
  16. return cache[key] = mult.apply(this, arr);
  17. }
  18. }
  19. })();

用高阶函数动态创建代理

  1. /**************** 计算乘积 *****************/
  2. var mult = function () {
  3. var a = 1;
  4. for (var i = 0, l = arguments.length; i < l; i++) {
  5. a = a * arguments[i];
  6. }
  7. return a;
  8. };
  9. /**************** 计算加和 *****************/
  10. var plus = function () {
  11. var a = 0;
  12. for (var i = 0, l = arguments.length; i < l; i++) {
  13. a = a + arguments[i];
  14. }
  15. return a;
  16. };
  17. var createProxyFactory = function (fn) {
  18. var cache = {};
  19. return function () {
  20. var key = [].join.call(arguments);
  21. if (cache[key]) {
  22. return cache[key];
  23. } else {
  24. return cache[key] = fn.apply(this, arguments);
  25. }
  26. }
  27. }
  28. var proxyMult = createProxyFactory(mult),
  29. proxyPlus = createProxyFactory(plus);
  30. alert(proxyMult(1, 2, 3, 4)); // 输出:24
  31. alert(proxyMult(1, 2, 3, 4)); // 输出:24
  32. alert(proxyPlus(1, 2, 3, 4)); // 输出:10
  33. alert(proxyPlus(1, 2, 3, 4)); // 输出:10