这是我参与更文挑战的第1天,活动详情查看: 更文挑战javascript
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。html
代理模式的关键是,当客户不方便直接访问一个对象或者不知足须要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。java
传统作法是小明直接把花送给小白,小白接收到花,代码以下:git
const Flower = function () {
return '玫瑰🌹'
}
const xiaoming = {
sendFlower: target => {
const flower = new Flower()
target.receiveFlower(flower)
}
}
const xiaobai = {
receiveFlower: flower => {
console.log('收到花', flower)
}
}
xiaoming.sendFlower(xiaobai)
复制代码
可是,小明并不认识小白,他想要经过小代,帮他打探小白的状况,在小白心情好的时候送花,这样成功率更高。代码以下:github
const Flower = function () {
return '玫瑰🌹'
}
const xiaoming = {
sendFlower: target => {
const flower = new Flower()
target.receiveFlower(flower)
}
}
const xiaodai = {
receiveFlower: flower => {
xiaobai.listenGoodMood().then(() => {
xiaobai.receiveFlower(flower)
})
}
}
const xiaobai = {
receiveFlower: flower => {
console.log('收到花', flower)
},
listenGoodMood: fn => {
return new Promise((reslove, reject) => {
// 10秒后,心情变好
reslove()
})
}
}
xiaoming.sendFlower(xiaodai)
复制代码
以上,小明经过小代,监听到小白心情的心情变化,选择在小白心情好时送花给小白。不只如此,小代还能够作如下事情:web
图片预加载时一种常见的技术,若是直接给img标签节点设置src属性,因为图片过大或网络不佳,图片的位置每每有一段时间时空白。ajax
const myImage = (() => {
const imgNode = document.createElement('img')
document.body.appendChild(imgNode)
return {
setSrc: src => {
imgNode.src = src
}
}
})()
myImage.setSrc('https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa98e67c4708449eb6894c7133d93774~tplv-k3u1fbpfcp-watermark.image')
复制代码
经过开发者工具把网速设置为 5kb/s时,会发如今很长一段时间内,图片位置是空白的。设计模式
下面用虚拟代理优化该功能,把加载图片的操做交给代理函数完成,在图片加载时,先用一张loading图占位,当图片加载成功后,再把它填充进img节点。缓存
代码以下:服务器
const myImage = (() => {
const imgNode = document.createElement('img')
document.body.appendChild(imgNode)
return {
setSrc: src => {
imgNode.src = src
}
}
})()
const loadingSrc = '../../../../img/loading.gif'
const imgSrc = 'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa98e67c4708449eb6894c7133d93774~tplv-k3u1fbpfcp-watermark.image'
const proxyImage = (function () {
const img = new Image()
img.onload = () => {
myImage.setSrc(img.src)
}
return {
setSrc: src => {
myImage.setSrc(loadingSrc)
img.src = src
}
}
})()
proxyImage.setSrc(imgSrc)
复制代码
上述代码有如下优势:
经过 proxyImage
控制了对 MyImage
的访问,在 MyImage
未加载成功以前,使用 loading
图占位;
践行单一职责原则,给 img
节点设置 src
的函数 MyImage
,预加载图片的函数 proxyImage
,都只有一个职责;
践行开放-封闭原则,给 img
节点设置 src
和预加载图片的功能,被隔离在两个对象里,它们能够各自变化不影响对方。
假设咱们要实现一个同步文件的功能,经过复选框,当复选框选中的时候,将该复选框对应的id传给服务器,告诉服务器须要同步 id 对应的文件。
思考一下,会发现,若是每选中一个复选框,就请求一次接口,假设 1s 内选中了 10 个复选框,那么就要发送 10 次请求。
能够经过虚拟代理来优化上述作法,新增一个代理,帮助复选框发起同步文件的请求,收集在这 1s 内的请求,1s 后再一块儿把这些文件 id 发送到服务器。
代码以下:
<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head>
<title></title>
</head>
<body>
a <input type="checkbox" value="a" />
b <input type="checkbox" value="b" />
c <input type="checkbox" value="c" />
d <input type="checkbox" value="d" />
<script type="text/javascript" src="index.js">
</script>
</body>
</html>
复制代码
const synchronousFile = cache => {
console.log('开始同步文件,id为:'+ cache.join('/'))
}
const proxySynchronousFile = (() => {
const cache = []
let timer
return id => {
console.log(id)
cache.push(id)
if (timer) {
return
}
timer = setTimeout(() => {
synchronousFile(cache)
clearTimeout(timer)
timer = null
cache.length = 0
}, 2000)
}
})()
const checkbox = document.getElementsByTagName('input')
Array.from(checkbox).forEach(i => {
console.log(i)
i.onclick = () => {
if (i.checked) {
proxySynchronousFile(i.value)
}
}
})
复制代码
在列表须要分页时,同一页的数据理论上只须要去后台拉取一次,能够把这些拉取过的数据缓存下来,下次请求时直接使用缓存数据
使用缓存代理实现上述功能,代码以下:
(async function () {
function getArticle (currentPage, pageSize) {
console.log('getArticle', currentPage, pageSize)
// 模拟一个ajax请求
return new Promise((resolve, reject) => {
resolve({
ok: true,
data: {
list: [],
total: 10,
params: {
currentPage,
pageSize
}
}
})
})
}
const proxyGetArticle = (() => {
const caches = []
return async (currentPage, pageSize) => {
const cache = Array.prototype.join.call([currentPage, pageSize],',')
if (cache in caches) {
return caches[cache]
}
const { data, ok } = await getArticle(currentPage, pageSize)
if (ok) {
caches[cache] = data
}
return caches[cache]
}
})()
// 搜索第一页
await proxyGetArticle(1, 10)
// 搜索第二页
await proxyGetArticle(2, 10)
// 再次搜索第一页
await proxyGetArticle(1, 10)
})()
复制代码
经过缓存代理,在第二次请求第一页的数据时,直接在缓存数据中拉取,无须再次从服务器请求数据。
上面根据实际场景介绍了虚拟代理和缓存代理的作法。
当咱们不方便直接访问某个对象时,找一个代理方法帮咱们去访问该对象,这就是代理模式。
可经过github源码进行实操练习。
但愿能对你有所帮助,感谢阅读~别忘了点个赞鼓励一下我哦,笔芯❤️
· 往期精彩 ·