JS-代理模式

介绍

代理是一个对象,它可以用来控制对本体对象的访问,它与本体对象实现了同样的接口,代理对象会把所有的调用方法传递给本体对象的;代理模式最基本的形式是对访问进行控制,而本体对象则负责执行所分派的那个对象的函数或者类,简单的来讲本地对象注重的去执行页面上的代码,代理则控制本地对象何时被实例化,何时被使用;

优点

1. 可以保护对象,代理对象可以代替本体被实例化,并使其可以被远程访问;

2. 优化性能,减少开销很大的对象

3. 缓存结果

实现

老规矩举个栗子来解释一下:小明喜欢上了隔壁班的小红,但是又不好意思去表白,所以就找舍友小李帮忙去送情书,那小李就是个代理

// 先声明小红对象
var girl = function (name) {
    this.name = name;
};

// 这是小明
var ming= function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        console.log("Hi " + girl.name + ", 小明送给你:" + gift);
    }
};

// 小李
var proxyLi = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        (new ming(girl)).sendGift(gift); // 给好基友打call
    }
};

var proxy = new proxyLi(new girl("美丽的小红"));
proxy.sendGift("一封情书");

//Hi 美丽的小红, 小明送给你:一封情书

代理模式的分类

代理模式根据职责的不同划分了不同分类,这里主要说一下在js中常见的几种

1.ES6的proxy

// 明星
let star = {
    name: 'CXK',
    age: 25,
    phone: '13910733521'
}

// 经纪人
let agent = new Proxy(star, {
    get: function (target, key) {
        if (key === 'phone') {
            // 保护隐私返回经纪人的手机号
            return '18611112222'
        }
        if (key === 'price') {
            // 明星不报价,经纪人报价
            return 5000000
        }
        return target[key]
    },
    set: function (target, key, val) {
        if (key === 'customPrice') {
            if (val < 4000000) {
                // 最低 400w
                throw new Error('这点钱不太够')
            } else {
                target[key] = val
                return true
            }
        }
    }
})

// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)

// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 8000000
//agent.customPrice = 2000000  // 报错:这点钱不太够
console.log('customPrice', agent.customPrice)


CXK
25
18611112222
5000000
customPrice 8000000

2.虚拟代理

2.1使用虚拟代理实现图片的预加载

在网页开发中,图片的预加载是一种比较常用的技术,如果直接给img标签节点设置src属性的话,如果图片比较大的话,或者网速相对比较慢的话,那么在图片未加载完之前,图片会有一段时间是空白的场景,这样对于用户体验来讲并不好,那么这个时候我们可以在图片未加载完之前我们可以使用一个loading加载图片来作为一个占位符,来提示用户该图片正在加载,等图片加载完后我们可以对该图片直接进行赋值即可;

// 方案一:不使用代理的预加载图片函数如下
var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    var img = new Image();
    img.onload = function(){
        imgNode.src = this.src;
    };
    return {
        setSrc: function(src) {
            imgNode.src = "loading.gif";
            img.src = src;
        }
    }
})();
// 调用方式
myImage.setSrc("pic.png");



//方案二:使用代理的预加载图片函数如下
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(){
        myImage.setSrc(this.src);
    };
    return {
        setSrc: function(src) {
            myImage.setSrc("loading.gif");
            img.src = src;
        }
    }
})();
// 调用方式
ProxyImage.setSrc("pic.png");

具体分析一下两种写法的优劣

方案一:创建img标签--->插入img标签-->创建img对象-->onloading-->返回设置图片对象

缺点:代码耦合严重,当需求调整不需要预加载图片的时候,又得修改整个对象

方案二:myImage中创建img标签--->myImage中插入img标签-->myImage中返回设置imgNode的src方法-->ProxyImage中创建img对象-->ProxyImage中书写onload方法-->ProxyImage中返回设置图片的方法

优点:两个函数各自负责一件事情,当不需要预加载的时候,直接调用本体方法即可

2.2 使用虚拟代理合并http请求

比如在做后端系统中,有表格数据,每一条数据前面有复选框按钮,当点击复选框按钮时候,需要获取该id后需要传递给给服务器发送ajax请求,服务器端需要记录这条数据,去请求,如果我们每当点击一下向服务器发送一个http请求的话,对于服务器来说压力比较大,网络请求比较频繁,但是如果现在该系统的实时数据不是很高的话,我们可以通过一个代理函数收集一段时间内(比如说2-3秒)的所有id,一次性发ajax请求给服务器,相对来说网络请求降低了, 服务器压力减少了;


<body>
  <div id="wrapper">
    <input type="checkbox" id="1"></input>1
    <input type="checkbox" id="2"></input>2
    <input type="checkbox" id="3"></input>3
    <input type="checkbox" id="4"></input>4
    <input type="checkbox" id="5"></input>5
    <input type="checkbox" id="6"></input>6
    <input type="checkbox" id="7"></input>7
    <input type="checkbox" id="8"></input>8
    <input type="checkbox" id="9"></input>9
  </div>
</body>

<script type="text/javascript">
  // 模拟http请求
  var synchronousFile = function (id) {
    console.log('开始同步文件,id 为: ' + id);
  };

  var inputs = document.getElementsByTagName('input')
  var wrapper = document.getElementById('wrapper')
  wrapper.onclick = function (e) {
    if (e.target.tagName === 'INPUT' && e.target.checked) {
      proxySynchronousFile(e.target.id)
    }
  }

  var proxySynchronousFile = (function () {
    var cacheIds = [], // 保存一段时间内需要同步的 ID
    timeId = 0
    return function (id) {
      if (cacheIds.indexOf(id) < 0) {
        cacheIds.push(id)
      }
      clearTimeout(timeId)
      timeId = setTimeout(() => { // 2 秒后向本体发送需要同步的 ID 集合
        synchronousFile(cacheIds.join(','))
        cacheIds = [] // 清空ID集合
      }, 2000)
    }
  })()
</script>

3.缓存代理

 缓存代理的含义就是对第一次运行时候进行缓存,当再一次运行相同的时候,直接从缓存里面取,这样做的好处是避免重复一次运算功能,如果运算非常复杂的话,对性能很耗费,那么使用缓存对象可以提高性能

// 计算乘法
var mult = function(){
    var a = 1;
    for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
        a = a*arguments[i];
    }
    return a;
};
// 计算加法
var plus = function(){
    var a = 0;
    for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
        a += arguments[i];
    }
    return a;
}
// 代理函数
var proxyFunc = 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);
    }
};
var proxyMult = proxyFunc(mult);
console.log(proxyMult(1,2,3,4)); // 24
console.log(proxyMult(1,2,3,4)); // 缓存取 24

var proxyPlus = proxyFunc(plus);
console.log(proxyPlus(1,2,3,4));  // 10
console.log(proxyPlus(1,2,3,4));  // 缓存取 10

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值