“这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战”前端
在面试高级前端时,每每会遇到一些关于设计模式的问题,每次都回答不太理想。恰逢8月更文挑战的活动,准备用一个月时间好好理一下关于设计模式方面的知识点,给本身增长点面试的底气。web
代码模式:为一个对象提供一个代用品或占位符,以便控制对它的访问。面试
代理模式的关键是,当程序不方便或者不知足须要去直接访问一个对象的时候,提供一个替身 对象来控制对这个对象的访问,程序实际上访问的是替身对象。替身对象对请求作出一些处理之 后,再把请求转交给本体对象。设计模式
代理模式的变体种类很是多,不必定都适合在JavaScript中使用,本文挑两种在JavaScript开发中最经常使用的两种代理来介绍,分别是虚拟代理和缓存代理。缓存
虚拟代理会把一些开销很大的对象,延迟到真正须要它的时候才去建立。这里用实现图片预加载功能的例子来介绍虚拟代理。markdown
图片预加载是一种经常使用的技术,若是直接给某个 img 标签节点设置 src 属性,因为图片过大或者网络不佳,图片的位置每每有段时间会是一片空白。常见的作法是先用一张 loading 图片占位,而后用异步的方式加载图片,等图片加载好了再把它填充到 img 节点里,这种场景就很适合使用虚拟代理。网络
先建立一个MyImage
类,用来生成一个 img 节点并将插入 body 中。app
class MyImage {
constructor() {
this.img = document.createElement('img');
document.body.appendChild(this.img);
}
setSrc(src) {
this.img.src = src;
}
}
复制代码
执行const myImage = new MyImage()
生成一个 img 节点并将插入 body 中,先在开发者工具中把网速调至Slow 3G,而后经过 执行myImage.setSrc('xxx')
给该 img 节点设置 src 属性,能够看到,在图片被加载好以前,页面中有段时间会是一片空白。异步
为了解决这个问题。写一个代理类ProxyImage
,经过这个代理类,实如今图片被真正加载好以前,页面中将出现一张 loading 图片占位, 来提示用户图片正在加载。工具
class ProxyImage {
constructor() {
this.myImage = new MyImage();
this.image = new Image();
this.image.onload = function () {
this.myImage.setSrc(this.image.src);
}
}
setSrc(src) {
this.myImage.setSrc('./loading.gif');
this.image.src = src;
}
}
const proxyImage = new ProxyImage();
proxyImage.setSrc(xxxx);
复制代码
经过ProxyImage
代理类间接地访问MyImage
类实例化生成的对象,ProxyImage
类控制了对MyImage
类实例化生成的对象访问,而且在此过程当中加入一些额外的操做,好比在真正的图片加载好以前,先把 img 节点的 src 设置为一张本地的 loading 图片。
执行proxyImage.setSrc(xxxx)
后,会先执行this.myImage.setSrc('./loading.gif')
给 img 节点的src 属性设置一张 loading 占位图的地址,同时把真正要展现的图片地址 xxxx 赋值给new Image()
建立的图片对象的 src 属性,在图片对象加载成功后再把图片地址 xxxx 经过this.myImage.setSrc(this.image.src)
从新赋值给 img 节点的 src 属性。
这样在真正要展现的图片加载成功以前,由MyImage
类实例化生成的 img 节点会一直展现 loading 的占位图,实现图片预加载的功能。
假如不用虚拟代理能够实现图片预加载的功能吗,固然能够,下面来实现一下:
class MyImage {
constructor() {
this.img = document.createElement('img');
document.body.appendChild(this.img);
this.image = new Image();
image.onload = function () {
this.img.src = this.image.src;
};
}
setSrc(src) {
this.img.src = './loading.gif';
this.image.src = src;
}
}
复制代码
咋看一下代码量还更少,代码更简单了。可是这么写违背了设计模式的单一职责原则,在MyImage
类中除了负责生成 img 节点并设置src,还要负责预加载图片。
假若有一张图片很是小,根本不须要预加载,使用预加载还会出现占位图一闪而过的问题。那怎么办呢?难道要从新写个MyImage
类,这会违背开放—封闭原则
实际上MyImage
类的功能只须要给 img 节点设置 src,预加载图片只是一个锦上添花的功能。假如把这个功能放在代理中,那么代理的做用在这里就体现出来了,代理负责预加载图片,预加载的操做完成以后,从新交给MyImage
类中来给 img 节点设置 src 。
这里并无改变或者增长 MyImage 的功能,只是经过代理,给MyImage
类添加了新的功能。这是符合开放—封闭原则。
给 img 节点设置 src 和图片预加载这两个功能,被隔离在两个类里,它们能够各自变化而不影响对方。况且就算有一天咱们再也不须要预加载,那么只须要改为请求本体而不是请求代理对象便可。