【Javascript】设计模式之代理模式

文章目录

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

1、简单代理模式的例子

小明给A送花的例子

不使用代理:

js 复制代码
var Flower = function() {};
var xiaoming = {
    sendFlower: function(target) {
        var flower = new Flower();
        target.receiveFlower(flower);
    }
}
var A = {
    receiveFlower: function(flower) {
        console.log('收到花' + flower);
    }
}
xiaoming.sendFlower(A);

引入代理B,通过B来个A 送花:

js 复制代码
var Flower = function () { };
var xiaoming = {
    sendFlower: function (target) {
        var flower = new Flower();
        target.receiveFlower(flower);
    }
}

// 代理B
var B = {
    receiveFlower: function (flower) {
        A.receiveFlower(flower)
    }
}

var A = {
    receiveFlower: function (flower) {
        console.log('A收到花' + flower);
    }
}

xiaoming.sendFlower(B);

2、保护代理和虚拟代理

2.1 保护代理

代理 B 可以帮助 A过滤掉一些请求,比如送花的人中年龄太大的或者没有宝马的,这种请求就可以直接在代理 B处被拒绝掉。这种代理叫作保护代理

2.1 虚拟代理

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

代码如下:

js 复制代码
var B = {
    receiveFlower: function (flower) {
        A.listenGoodMood(function () { // 监听 A 的好心情
            var flower = new Flower(); // 延迟创建 flower 对象
            A.receiveFlower(flower);
        });
    }
};

3、虚拟代理实现图片预加载

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

创建一个 img 标签,并且提供一个对外的 setSrc 接口:

js 复制代码
var myImage = (function () {
    var imageNode = document.createElement('img');
    document.body.appendChild(imageNode);
    return {
        setSrc: function (src) {
            imageNode.src = src;
        }
    }
})()

myImage.setSrc('https://img0.baidu.com/it/u=1371113496,4161662645&fm=26&fmt=auto');

问题:

在网速较差时,图片被加载好之前,页面中有一段长长的空白时间

解决:

现在开始引入代理对象 proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中

将出现一张占位的菊花图 loading.gif,来提示用户图片正在加载

代码如下:

js 复制代码
var myImage = (function () {
    var imageNode = document.createElement('img');
    document.body.appendChild(imageNode);
    return {
        setSrc: function (src) {
            imageNode.src = src;
        }
    }
})()

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

    return {
        setSrc: function(src) {
            myImage.setSrc('./assets/loading.gif');
            img.src = src;
        }
    }
})()

proxyImage.setSrc('https://img0.baidu.com/it/u=1371113496,4161662645&fm=26&fmt=auto');

4、代理和本体接口的一致性

代理对象和本体都对外提供了 setSrc 方法,说明代理对象和本体是一致的, 代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别,这样做有两个好处:

1、用户可以放心地请求代理,他只关心是否能得到想要的结果

2、在任何使用本体的地方都可以替换成使用代理

5、缓存代理

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

缓存代理例子------计算乘积

创建一个用于求乘积的函数

js 复制代码
var mult = function () {
  var a = 1;
  for (var i = 0; i < arguments.length; i++) {
    a = a * arguments[i];
  }
  return a;
};

加入缓存代理函数:

js 复制代码
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));
  };
})();

测试:

js 复制代码
var res1 = proxyMult(2, 3, 6);
var res2 = proxyMult(2, 3, 6);
console.log(res1); // 24
console.log(res2); // 24 本体 mult 函数并没有被计算,proxyMult 直接返回了之前缓存好的计算结果

通过增加缓存代理的方式,mult 函数可以继续专注于自身的职责------计算乘积,缓存的功能是由代理对象实现的

6、用高阶函数动态创建代理

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

现在这些计算方法被当作参数传入一个专门用于创建缓存代理的工厂中, 这样一来,我们就可以为乘法、加法、减法等创建缓存代理,代码如下:

js 复制代码
/**************** 计算乘积 *****************/
var mult = function () {
  var a = 1;
  for (var i = 0; i < arguments.length; i++) {
    a = a * arguments[i];
  }
  return a;
};
/**************** 计算和 *****************/
var plus = function () {
  var a = 0;
  for (var i = 0; i < arguments.length; 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));
  };
};

测试:

js 复制代码
var proxyMult = createProxyFactory(mult);
console.log(proxyMult(2, 3, 6));
console.log(proxyMult(2, 3, 6));

var proxyPlus = createProxyFactory(plus);
console.log(proxyPlus(2, 3, 6));
console.log(proxyPlus(2, 3, 6));

7、小结

代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理。虽然代理

模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟

相关推荐
阿伟来咯~31 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端36 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱39 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨1 小时前
在JS中, 0 == [0] 吗
开发语言·javascript
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
aPurpleBerry3 小时前
JS常用数组方法 reduce filter find forEach
javascript