(二)代理模式 |小册免费学

一、代理模式的概念

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

出自《JavaScript设计模式与开发实践》的官方定义。

代理模式是一种非常有意义的模式,在生活中可以找到很多代理模式的场景。比如,明星都有经纪人作为代理。如果想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演出的细节和报酬都谈好之后,再把合同交给明星签。
哈哈,这样一类比,代理模式就变得形象容易理解啦。代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身 对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。

二、常见的代理模式有哪几种?

2.1 远程代理

远程代理是常用的代理模式,就是为不同地址空间的对象提供一个局域网代表对象。比如,有一家全国连锁店,总店想监控各个分店的情况,就可以通过监控器,使之能直观的了解店内信息。

2.2 保护代理

保护代理,就是控制对一个对象的访问权限。沿用上述明星经纪人的例子,其中经纪人就是这保护代理,合同谈妥前都是保护明星不被打扰,隐私不受侵犯。

2.3 虚拟代理

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

2.4 缓存代理

缓存代理比较好理解,它可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果,不必重新计算。这种方式,是典型的“用空间换时间”优化手段。

三、结合前端应用案例来加深理解

3.1 事件代理

JavaScript中我们常用的事件代理(也叫事件委托),就是属于代理模式,主要利用事件冒泡的特性,将子元素的事件监听委托父元素。这样一来,触发事件的监听函数只需要在父元素div绑定一次即可,不需要在子元素绑定 N 次,很大程度上提高了代码的性能。
实现点击事件代理的代码如下:

// 获取父元素
const father = document.getElementById('father');
// 给父元素绑定一次监听函数
father.addEventListener('click', function(e){
    // 识别是否是目标子元素
    if(e.target.tagName == 'A'){
        // 监听函数的主体
        e.preventDefault()
        alert(`我是${e.target.innerText}`)
    }
})
复制代码

3.2 图片预加载

图片预加载,前面简介了下,该优化手段属于代理模式的虚拟代理。之所以使用预加载,主要是为了避免网络不好、或者图片太大时,页面长时间白屏给用户带来不好的体验。实现方案:先用一张菊花图 loading.gif 图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到 img 节点里。具体代码如下:

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( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
            img.src = src;
        }
    }
})();
proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );
复制代码

3.3 ES6的Proxy

我们平时使用的ES6 Proxy代理可以划归于代理模式的保护代理。为什么这样说呢? Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式。
开发时,我们会在对象上面设置内部属性,属性名的第一个字符使用下划线开头,表示这些属性不应该被外部使用。结合 Proxy 的 get 和 set,我们便可以做到保护内部属性不被外部读写。具体代码实现如下:

const handler = {
  get (target, key) {
    invariant(key, 'get');
    return target[key];
  },
  set (target, key, value) {
    invariant(key, 'set');
    target[key] = value;
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property
复制代码

四、总结

我们结合具体的使用场景,相信大家已经充分理解代理模式,它的套路就是:A不能直接访问B,A就需要借助一个代理帮手来访问B,这个帮手就是代理器。

本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享