代理模式
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问
保护代理与虚拟代理
保护代理:代理帮主体过滤掉一些请求
虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建
虚拟代理实现图片预加载
图片加载完成前的 loading 展位图
1const myImg = (function () {2 const imgNode = document.createElement('img')3 document.body.appendChild(imgNode)45 return {6 setSrc(src) {7 imgNode.src = src8 },9 }10})()1112const proxyImg = (function () {13 const img = new Image()14 img.onload = function () {15 myImg.setSrc(this.src)16 }1718 return {19 setSrc(src) {20 myImg.setSrc('./img/loading.gif')21 img.src = src22 }23 }24})()2526proxyImg.setSrc('http://abigimage.com/abigimage.png')
可进一步改进,封装成组件,暴露 setLoadingSrc 和 setSrc
代理和主体接口的一致性
如果有一天我们不再需要预加载图片,保证接口的一致性可以让我们在任何使用代理的地方切换成本体
在 JavaScript 中,如果代理对象和本体对象都是一个函数,函数必然都能被执行,则可认为他们有一致的“接口”
1const myImg = (function () {2 const imgNode = document.createElement('img')3 document.body.appendChild(imgNode)45 return function (src) {6 imgNode.src = src7 }8})()910const proxyImg = (function () {11 const img = new Image()12 img.onload = function () {13 myImg(this.src)14 }1516 return function (src) {17 myImg('./img/loading.gif')18 img.src = src19 }20})()
虚拟代理合并 http 请求
一个文件列表,点击 checkbox 上传相应文件,如果选中过快,频繁的网络请求是一笔极大的开销
可以将多次网络请求(几秒钟内的)合并成一次,减小服务器的压力
1const uploadFiles = function (ids) {2 // 对 ids 数组相应的文件进行处理3 // 发送一次请求4}56const proxyUploadFiles = (function () {7 const ids = []8 let timer910 return function (id) {11 ids.push(id)1213 if (timer) {14 return15 }1617 timer = setTimeout(() => {18 uploadFiles(ids)1920 clearTimeout(timer)21 timer = null22 ids.length = 023 }, 2000)24 }25})()2627// 事件委托
缓存代理
缓存计算结果:
1const calculate = function (...args) {2 let result3 // 一些消耗巨大的运算4 return result5}67const proxyCalculate = (function () {8 const cache = {}910 return function (...args) {11 const key = args.join(',')1213 if (cache[key]) {14 return cache[key]15 }1617 cache[key] = calculate(...args)18 return cache[key]19 }20})()
进一步解耦,修改为创建代理的工厂,更有通用性
1const createProxy = function (fn) {2 const cache = {}34 return function (...args) {5 const key = args.join(',')67 if (cache[key]) {8 return cache[key]9 }1011 cache[key] = fn(...args)12 return cache[key]13 }14}1516const mult = function (...args) {17 args.reduce((current, next) => current * next)18}1920const plus = function (...args) {21 args.reduce((current, next) => current + next)22}2324const proxyMult = createProxy(mult)25const proxyPlus = createProxy(plus)2627console.log(proxyMult(1, 2, 3))28console.log(proxyPlus(1, 2, 3))
缓存请求:
略
ES6 中的代理模式
new Proxy() 这个实例就是对象的代理
其他代理模式
防火墙代理:控制网络资源的访问,保护主题不让“坏人”接近。
远程代理:为一个对象在不同的地址空间提供局部代表,在 Java 中,远程代理可以是另一个虚拟机中的对象。
保护代理:用于对象应该有不同访问权限的情况。
智能引用代理:取代了简单的指针,它在访问对象时执行一些附加操作,比如计算一个对象被引用的次数。
写时复制代理:通常用于复制一个庞大对象的情况。写时复制代理延迟了复制的过程,当对象被真正修改时,才对它进行复制操作。写时复制代理是虚拟代理的一种变体,DLL(操作系统中的动态链接库)是其典型运用场景。