JavaScript设计模式 - 代理模式

本文介绍了代理模式的概念,包括保护代理和虚拟代理,重点讲解了虚拟代理在图片预加载中的应用。通过虚拟代理,可以避免因图片加载延迟造成的用户体验问题。此外,还讨论了代理模式的意义,如遵循单一职责原则,保持代理和本体接口一致,以及在实际开发中如何灵活运用。最后,提到了缓存代理在减少重复运算上的作用,并列举了其他类型的代理模式。
摘要由CSDN通过智能技术生成

1. 代理模式的简单理解

定义:为一个对象提供一个代用品或占位符,以便控制对它的访问
代理模式的关键:当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象

2. 常用的代理模式

  1. 保护代理
  2. 虚拟代理

2.1 保护代理

代理B可以帮本体A过滤掉一些请求,比如有人想通过B送花给A,但是B可以过滤掉送花中年龄大或者比较穷的,这些请求可以直接在代理B处被拒绝掉

2.2 虚拟代理

虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建
例如:假设现实中的花价格不菲,导致在程序世界中,new Flower也是一个代价昂贵的操作,那么我们可以把new Flower的操作交给代理B去执行,代理B会选择在A心情好时再执行new Flower

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

2.2.1 虚拟代理实现图片预加载

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

const myImage = (function() {
    let imgNode = document.createElement('img')
    document.body.appendChild(imgNode)

    return {
        setSrc: function(src) {
            imgNode.src = src
        }
    }
})()

const proxyImage = (function() {
    let img = new Image
    img.onload = function() {
        myImage.setSrc(this.src)
    }
    return {
        setSrc: function(src) {
            myImage.setSrc('file:// /C:/Users/lijin/Desktop/新建文件夹/微信图片_20211020164507.jpg')
            img.src = src
        }
    }
})()

proxyImage.setSrc('https://img1.baidu.com/it/u=1677307154,4128866455&fm=26&fmt=auto')

3. 代理的意义

不使用代理的预加载图片函数实现如下:

const myImage = (function() {
    let imgNode = document.createElement('img')
    document.body.appendChild(imgNode)

    const img = new Image
    img.onload = function() {
        imgNode.src = img.src
    }
    return {
        setSrc: function(src) {
            imgNode.src = 'file:// /C:/Users/lijin/Desktop/新建文件夹/微信图片_20211020164507.jpg'
            img.src = src
        }
    }
})()
myImage.setSrc('https://img1.baidu.com/it/u=1677307154,4128866455&fm=26&fmt=auto')

为了说明代理的意义,下面我们引入一个面向对象设计的原则 — 单一职责原则

单一职责原则:就一个类(通常也包含对象和函数等)而言,应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起他变化的原因可能会有多个。面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏。

4. 代理和本体接口一致性

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

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

5. 缓存代理

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

6. 其他代理模式

  1. 防火墙代理:控制忘了资源的访问,保护主体不让“坏人”接近
  2. 远程代理:为一个对象在不同的地址空间提供局部代表,在Java中,远程代理可以是另一个虚拟机中的对象、
  3. 保护代理:用于对象应该有不同访问权限的情况
  4. 智能引用代理:取代了简单的指针,他在访问对象时执行一些附加操作,比如计算一个对象被引用的次数
  5. 写时复制代理:通常用于复制一个庞大对象的情况。写时复制代理延迟了复制的过程,当对象被真正修改时,才对他进行复制操作,写时复制代理是虚拟代理的一种变体

7. 小结

代理模式包括许多小分类,在JavaScript开发中最常用的是虚拟代理和缓存代理。虽然代理模式非常有用,但在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问对象的时候,再编写代理也不迟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值