3

结构型-代理模式 - 风吹De麦浪

 1 year ago
source link: https://www.cnblogs.com/longbensong/p/17255083.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

  代理是一个中间者的角色,如生活中的中介,出于种种考虑/限制,一个对象不能直接访问另一个对象,需要一个第三者(中间代理)牵线搭桥从而间接达到访问目的,这样的就是代理模式。

es6 中的代理

  es6 的 proxy 就是上面说的代理模式的实现,es6 帮我们在语法层面提供了这个新的api,让我们可以很轻松的就使用了代理模式。

const p = new Proxy(target, handler)target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)handler:一个通常以函数作为属性的对象

 proxy 实例

const handler = { get: function(obj, prop) { return prop in obj ? obj[prop] : 37; }}; const p = new Proxy({}, handler);p.a = 1;p.b = undefined; console.log(p.a, p.b); // 1, undefinedconsole.log('c' in p, p.c); // false, 37

 应用实践-模拟代理模式

  代理模式的应用非常常见,既可以是为了加强控制、拓展功能、提高性能,也可以仅仅是为了优化我们的代码结构、实现功能的解耦。无论是出于什么目的,这种模式的套路就只有一个—— A 不能直接访问 B,A 需要借助一个帮手来访问 B,这个帮手就是代理器。需要这种代理器的就是代理模式的应用场景。

通常开发中最常见的代理类型:事件代理、虚拟代理、缓存代理、保护代理

  • 事件代理:代理 DOM
  • 虚拟代理:代理 DOM
  • 缓存代理:代理函数
  • 保护代理:代理对象

事件代理是代理模式最常见的一种应用方式,它的场景是一个父元素下有多个子元素

<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="yes" name="apple-mobile-web-app-capable"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta content="telephone=no,email=no" name="format-detection"> <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> </head> <body> <p>图片列表--事件代理</p> <ul id="ul_wrapper"> <li> 1、<img id="img_1" src="" alt=""> </li> <li> 2、<img id="img_2" src="" alt=""> </li> <li> 3、<img id="img_3" src="" alt=""> </li> <li> 4、<img id="img_4" src="" alt=""> </li> <li> 5、<img id="img_5" src="" alt=""> </li> <li> 6、<img id="img_6" src="" alt=""> </li> <li> 7、<img id="img_7" src="" alt=""> </li> <li> 8、<img id="img_8" src="" alt=""> </li> </ul> <script> // 自己找个base64,拷贝上来太长了 let defualtSrc = `` let initPage = (function () { document.querySelectorAll('img').forEach(item => { item.src = defualtSrc }) })(); document.querySelector('#ul_wrapper').addEventListener('click', function (e) { if (e.target.nodeName === 'IMG') { alert('图片被点击') } }, false) </script></body> </html>

  缓存代理可以避免重复的计算

<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="yes" name="apple-mobile-web-app-capable"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta content="telephone=no,email=no" name="format-detection"> <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> </head> <body> <p>图片列表--缓存代理</p> <button type="button">计算</button> <div> <label>结果:</label><input type="text"> </div> <script> /* 有效的减少计算; 工具函数 */ const addAll = function (...args) { console.log('进行了一次新计算') let result = 0 const len = args.length for (let i = 0; i < len; i++) { result += args[i] } return result } let proxyAddAll = (function () { const resultCache = {} return function (fn, ...args) { const key = args.join('') if (resultCache[key]) { return resultCache[key] } return resultCache[key] = fn.apply(this, args) } })() // 123456 参数相同,只是第一次运算的时候,打印了一次进行了一次新计算 console.log(proxyAddAll(addAll, 1, 2, 3, 4, 5, 6)) console.log(proxyAddAll(addAll, 1, 2, 3, 4, 5, 6)) console.log(proxyAddAll(addAll, 1, 2, 3, 4, 5, 6)) // 1234567 因为是一个全新的参数所以打印了一次进行了一次新计算 console.log(proxyAddAll(addAll, 1, 2, 3, 4, 5, 7)) </script></body> </html>

  图片预加载,预加载主要是为了避免网络不好、或者图片太大时,页面长时间给用户留白的尴尬。原理也很简单创建一个图片实例指向图片真实地址,当完成加载时,把占位图的地址替换成真实的地址,这个时候浏览器会直接从缓存里面拿。

<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"> <meta content="yes" name="apple-mobile-web-app-capable"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta content="telephone=no,email=no" name="format-detection"> <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> </head> <body> <p>图片列表--虚拟代理</p> <ul> <li> 1、<img id="img_1" src="" alt=""> </li> <li> 2、<img id="img_2" src="" alt=""> </li> <li> 3、<img id="img_3" src="" alt=""> </li> <li> 4、<img id="img_4" src="" alt=""> </li> <li> 5、<img id="img_5" src="" alt=""> </li> <li> 6、<img id="img_6" src="" alt=""> </li> <li> 7、<img id="img_7" src="" alt=""> </li> <li> 8、<img id="img_8" src="" alt=""> </li> </ul> <script> // 替换成你的base64,拷贝上来太长 let defualtSrc = "" let initPage = (function () { document.querySelectorAll('img').forEach(item => { item.src = defualtSrc }) })(); // 设置图片地址 function setImgUrl(dom, src) { dom.src = src; } // 中间的代理图片地址 function proxyImg(element, url) { // 创建一个虚拟Image实例 const virtualImage = new Image() virtualImage.onload = function () { setImgUrl(element, url) } virtualImage.src = url } function preLoadImg() { const urlList = [ 'https://t7.baidu.com/it/u=2621658848,3952322712&fm=193&f=GIF', 'https://t7.baidu.com/it/u=4080826490,615918710&fm=193&f=GIF', 'https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF', 'https://t7.baidu.com/it/u=3713375227,571533122&fm=193&f=GIF', 'https://t7.baidu.com/it/u=801209673,1770377204&fm=193&f=GIF', 'https://t7.baidu.com/it/u=1856946436,1599379154&fm=193&f=GIF', 'https://t7.baidu.com/it/u=1010739515,2488150950&fm=193&f=GIF', 'https://t7.baidu.com/it/u=813347183,2158335217&fm=193&f=GIF'] document.querySelectorAll('img').forEach((element, index) => { proxyImg(element, urlList[index]) element.src = defualtSrc }) } setTimeout(() => { preLoadImg() }, 0.5 * 1000); /* 核心: 有个虚拟的实例去请求地址,拿到之后替换到真实的dom */ </script></body> </html>

  可以通过es6 的proxy 的get、set 访问器实现

const handler = { get: function(obj, prop) { return prop in obj ? obj[prop] : '你不能访问'; }}; const p = new Proxy({}, handler);p.a = 1;p.b = undefined; console.log(p.a, p.b); // 1, undefinedconsole.log('c' in p, p.c); // false, 你不能访问
  A 不能直接访问 B,A 需要借助一个帮手来访问 B,这个帮手就是代理器,通常开发中最常见的四种代理类型:事件代理、虚拟代理、缓存代理、保护代理;
  1.  事件代理:事件冒泡,代理 DOM
  2. 虚拟代理:通过Image加载图片,代理 DOM
  3. 缓存代理:缓存计算结果,代理函数
  4. 保护代理:get,set保护核心数据,代理对象

如果您觉得阅读本文对您有帮助,请点一下推荐按钮,您的推荐将是我最大的写作动力,欢迎各位转载!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK