JavaScipt设计模式初探-代理模式

Posted 白瑕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScipt设计模式初探-代理模式相关的知识,希望对你有一定的参考价值。

文章目录


前言

代理模式初探, 后面会更新缓存代理和保护代理之类.
代理模式(Proxy): 某些原因需要给某对象提供一个代理以控制对该对象的访问. 这时, 访问对象不适合或者不能直接引用目标对象, 代理对象作为访问对象和目标对象之间的中介.
这种模式在现实生活中也十分常见, 但我觉得中介结构这种双向奔赴的, 其实对于抽象对象和真实对象的区分并不友好, 因为双方都可以主动.

所以我要举的例子是求爱)


一、举例_送花

我看了好多案例…挑了这个来记, 我觉得这个比较好懂一些)
男生, 要给女生送花, 但是他是一个胆小且内向的男孩子, 不好意思去亲自送, 所以就托花店去送.

基本逻辑就是: 代理proxyObj拿到男生boy那里的送花方法sendGift, 然后在代理proxyObj自己这里调用送花方法sendGift(注意不是"男生在代理处调用方法"而是直接由代理去调用), 拿到各个参数, 由自己去送花, 达到代男生送花的目的.
常规模式下, 我们会比较倾向于让男生自己去送花, 这样虽然代码冗杂度稍高, 但是写起来方便, 而使用代理模式的时候往往不具备这样做的条件.

是的, 在常规写法不可行的情况下再使用代理模式, 而不是能用代理模式就用代理模式.

//女生
var girl = function (name) 
  this.name = name;


//男生
var boy = function (girl) 
  this.girl = girl;
  this.sendGift = function (gift) 
    console.log("hey, " + this.girl.name + ", 这是某男生送你的" + gift)
  


//花店派送员, 他需要知道目标是谁, 需要帮忙做什么, 所以传入girl和sendGift
var proxyObj = function (girl) 
  this.girl = girl;
  this.sendGift = function (gift)    //花店员工知道了女生的名字, 开始送花
    (new boy(this.girl)).sendGift(girl);  
    //new boy(this.girl),得到 girl: girl函数, sendGift: sendGift函数 , 然后调用这个对象的sendGift方法达成目的: 帮男生送花
    //注意这步是代理proxyObj调用了送花方法senGift, 送花者并非男生
  

var girl = new girl("女生a");
var proxy = new proxyObj(girl); 
proxy.sendGift("不可名状")

额, 这句可能会有些疑惑, 割出来说下:

(new boy(this.girl)).sendGift(girl);

用类比的方法来看, 先看这个

function fun1(name) 
  this.name = name;

let xx = new fun1('baiX');
console.log(xx); //  name:'baiX' 

那么稍微变一下:

var boy = function (girl) 
  this.girl = girl;

let xx = new boy(girl);
console.log(xx);  
//  girl: girl函数, sendGift: sendGift函数 

其实就是:

new 函数(函数的参数);

代理模式的特点: 代理对象与真实对象具有相同行为.
比如此处, 分别为真实对象男生和代理对象店员定义了sendGift, 但是店员的sendGift里也并不是自己做了一个方法去送花, 而是调用了男同学送花的方法:

this.sendGift = function (gift) 
  (new boy(this.girl)).sendGift(girl);  

这样就可以满足上面说的代理模式特点 “代理对象与真实对象具有相同行为”, 店员送花的行为和男同学送花的行为必是一样的, 因为两者共用一个方法.

如果按照正常逻辑选择男生先将花送给店员, 再由店员转送女生, 那么代码冗余, 但是常规情况下就这么写, 代理模式只在常规情况不方便时使用.


二、举例_图片懒加载改造

老实说, 一开始没太看懂这个例子…
我大概花了半个多小时才基本看懂, 这确实和我的代码习惯很不一样, 用我固有的思路也不是那么容易理解.
额, 其实主要原因是刚开始我没有想到异步上, 所以…它的整个执行逻辑在我看来是混乱的.

1.普通写法-图片懒加载

就是这个原版没看懂, 先去看了代理模式改造才有点头绪(所以建议先去看下面一段代理模式的).
其实和代理模式的思路高度相似, 唯一不同的就是负责改图片的对象;

完整代码:

window.onload = function () 
  var myImage = (function () 
  
    var imgNode = document.createElement("img"); //创建img
    document.body.appendChild(imgNode);        //增加img
    var img = new Image();                   //创建Image对象, 这个对象有src属性
    
    img.onload = function ()             //当真实(要加载的)图片加载完毕后触发
      setTimeout(() =>                           
        imgNode.src = this.src;        //this指向img元素, src也就是setSrc里的img.src = src;
      , 2000);
    
    
    return 
      setSrc: function (src)        
         //代理图片      
         imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif";
         img.src = src;
      
    
    
  )()
  myImage.setSrc("https://www.baidu.com/img/bd_logo1.png")  //真实图片

myImage.setSrc调用后直接传入真实图片地址, 此时仅仅会将imgNode.src赋值加载gif, 而对img.src赋值的真(要加载的)图片地址并不会渲染, 直到img.onload执行, imgNode.src被赋值存储了真(要加载的)图片地址的img.src, 图片才显示出来, 所以图片在加载2s后显示.


2.代理模式_图片懒加载

先读懂是什么个思路再去看代理模式的事情吧…
想到是不是牵扯到一个异步的问题, window.onload内先由上向下执行, 但是直到执行到proxyImage之前似乎都不会对页面有甚麽视觉上的影响.

完整代码:

window.onload = function () 

  //闭包的真实对象
  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 ()   //加载完毕触发方法
      setTimeout(() => 
        myImage.setSrc(this.src);//this == img    
      , 2000)
    
    return 
      setSrc: function (src) 
      //这步只是调用了myImage的setSrc方法, 而并非myImage执行了setSrc
        myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif");
        img.src = src  //真实图片
        
      
  )()

  //调用, 给代理对象设置真实图片
  proxyImage.setSrc("https://www.baidu.com/img/bd_logo1.png")

proxyImage之后, 调用proxyImagesetSrc, 看向proxyImagesetSrc, 此时会调用myImagesetSrc导致imgNodesrc成为加载gif.
而同时myImage.setSrc(this.src)设置的this.srcimg.src此时拥有了真正的图片地址(要加载的图片地址), 但是还不会呈现.

var proxyImage = (function () 
  var img = new Image();
  return 
    setSrc: function (src) 
      myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif");
      img.src = src;
    
  
)()

这种情况直到proxyImage的计时器完成才有所改变, myImage.setSrc被再次调用, 传入了要加载的图片地址: img.onload的this.src, 即img.src,

var proxyImage = (function () 
  img.onload = function ()   //加载完毕触发方法
    setTimeout(() => 
      myImage.setSrc(this.src);  //this == img    
    , 2000)
  
)()

此时imgNode.src被赋值为img.src即要加载的图片地址:

var myImage = (function () 
  var imgNode = document.createElement("img");
    //document.body.appendChild(imgNode);
      return 
        setSrc: function (src) 
          imgNode.src = src;
        
      
)()

这样看来img对象在这个过程中担任一个存储的作用, 请求要用的图片并且直至其加载好.
所以2s后imgNode.src改变, 呈现要加载的图.


三、类比

这个懒加载并不需要用代理模式, 完全可以直接在myImage执行监听onload事件然后替换图片, 但是用代理实现也可以.
还是记住那个特点就好: 代理对象与真实对象具有相同行为.
图片懒加载的例子和送花的例子, 相同之处即都遵循该特点, 送花的前面说了这里不提, 懒加载中有myImageproxyImage这两个概念:

var myImage = (function () )()
var proxyImage = (function () )()

myImage中的imgNode.src控制着页面的图片显示, 所以它是需要被改变的, 这个例子更像是一种…雌雄同体的状态, 如果和送花案例比的话.

imgNode需要被改变, 它是目标, 它是女生.
myImage提出方法, 它是发起者, 它是男生.
proxyImage办事, 它是代理人, 它是店员.

但是imgNode就在myImage里, 所以我说他像是一种雌雄同体的状态…
依据代理模式特点和送花案例, proxyImage必须调用或者间接调用(在自己的方法里调用)myImage的方法, 否则将不遵循代理模式特点.

那么proxyImage对象必要调用myImagesetSrc方法去设置imgNode.src:
这是页面加载时的首次调用, 呈现加载中图片:

//proxyImage内
return 
  setSrc: function (src) 
  //只是调用myImage的setSrc方法, 并非myImage调用setSrc
  myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif");
  img.src = src;
  

而2s后下一次代理调用出现(我们假设2s后图片加载完毕需要呈现):
img.src此时存有加载好的图片地址

//proxyImage内
img.onload = function () 
  setTimeout(() => 
  
    myImage.setSrc(this.src);  //this.src即img.src
     
  , 2000)

呈现加载好的图片.


总结

话说这个还是我最后一次面试的时候被问到的, "你了解js的代理模式吗?"我以前确实听说过, 但是…没去学啊哈哈哈哈.

我知道, 但是我没有去做, 就好像我知道天灾要来了, 但是直到城市被毁我都没想起来和城市里的任何一个人说.
就是有点后悔, 我当时要是高低搞两句出来, 说不定会被高看一眼呢?

以上是关于JavaScipt设计模式初探-代理模式的主要内容,如果未能解决你的问题,请参考以下文章

JavaScipt设计模式初探-代理模式 虚拟代理

店员激励系统开发店员激励系统模式详解

设计模式 javascipt

java反射初探

论 HTTP 性能,Go 与 .NET Core 一争雌雄

用硒进行IP欺骗/购买共享代理?