javascript设计模式实践之代理模式--图片预加载
图片的预加载就是在加载大图片前,先显示一个loading.gif,就算在网络比较慢的时候也能让人知道正在加载,总比啥反应都没有强。
下面这段代码就是预加载的一个简单的实现,假设先不处理加载图片时的onError,onAbort,超时的问题。
只关注代码的结构。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <button id='btnLoadImg'>加载图片</button> <br> <div id='imgContainer'> </div> <br> <script type='text/javascript' src="./jquery-1.11.3.min.js"></script> <script type='text/javascript'> $(document).ready(function(){ $('#btnLoadImg').bind('click', doLoadImg); }); function doLoadImg(){ var eleImg = createImgElement(); document.getElementById('imgContainer').appendChild(eleImg); loadImg(eleImg, 'http://img.wanchezhijia.com/A/2015/3/20/17/11/de63f77c-f74f-413a-951b-5390101a7d74.jpg'); } //创建img标签 //这里用自执行函数加一个闭包,是为了可以创建多个id不同的img标签。 var createImgElement = (function(){ var index = 0; return function() { var eleImg = document.createElement('img'); eleImg.setAttribute('width', '200'); eleImg.setAttribute('heght', '150'); eleImg.setAttribute('id', 'img' + index++); return eleImg; }; })(); //预加载图片 //给img标签设一个加载图片,通过Image对象预先加载实际图片加载完成后设到img标签上 function loadImg(img, src) { var imgCache = new Image(); imgCache.onload = function(){ img.src = this.src; }; img.src = 'loading.gif'; imgCache.src = src; } </script> </body> </html>
以下预加载的代码功能上是满足了,但是它的职责包含了预加载和加载两个职责,违反了“单一职责原则”。所谓的职责就是“会发生的变化”,如果网速的问题不再是问题或者加载的图片的分辨率被控制在很小的时候等,需要去掉预加载的功能,这时候就要修改loadImg的代码,就要重新跑所有相关的测试,即违反了“开闭原则”,又增加测试工作。
function loadImg(img, src) { var imgCache = new Image(); imgCache.onload = function(){ img.src = this.src; }; img.src = 'loading.gif'; imgCache.src = src; }
加载和预加载其实就是代理模式的一种,代理模式可以理解成,你会开车但是因为某种原因只能找人代理开车,因为不了解MM的喜好找人代理送花。
将预加载功能改成代理模式可以理解成:本体先显示个门面,找代理去加载,加载完了,告诉本体,本体把图片贴上去就行了。
既然如此,本体的职责就很简单了,就往门面上贴。
加载本体函数:
function loadImg(img, src) { img.src = src; }
做一个预加载代理函数:
function loadImgProxy(img, src){ var imgCache = new Image(); imgCache.onload = function(){ loadImg(img, this.src); }; loadImg(img, 'loading.gif'); imgCache.src = src; }
在代理函数中,先让本体加载loading.gif,等大图加载完了再让本体加载实际图片。
代理还是和本体函数的接口参数是一致的,职责分的比较清晰,如果到时候要把预加载去掉,也不需要修改本体和代理的代码,只需要在调用的地方把代理的名字换成本体的名字即可。
可以适当的把代理函数改一下,可以让其适应加载多个图片的场景,其实就是做一个闭包,把缓存Image对象变成私有即可:
var loadImgProxy = (function(){ var imgCache = new Image(); return function(img, src){ imgCache.onload = function(){ loadImg(img, this.src); }; loadImg(img, 'loading.gif'); imgCache.src = src; }; })();
修改后的完整代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <button id='btnLoadImg'>加载图片</button> <br> <div id='imgContainer'> </div> <br> <script type='text/javascript' src="./jquery-1.11.3.min.js"></script> <script type='text/javascript'> $(document).ready(function(){ $('#btnLoadImg').bind('click', doLoadImg); }); function doLoadImg(){ var eleImg = createImgElement(); document.getElementById('imgContainer').appendChild(eleImg);
//使用代理函数进行加载
//如果某一天不需要预加载了,就把loadImgProxy换成loadImg即可 loadImgProxy(eleImg, 'http://img.wanchezhijia.com/A/2015/3/20/17/11/de63f77c-f74f-413a-951b-5390101a7d74.jpg'); } //创建img标签 //这里用自执行函数加一个闭包,是为了可以创建多个id不同的img标签。 var createImgElement = (function(){ var index = 0; return function() { var eleImg = document.createElement('img'); eleImg.setAttribute('width', '200'); eleImg.setAttribute('heght', '150'); eleImg.setAttribute('id', 'img' + index++); return eleImg; }; })();
//加载图片本体函数 function loadImg(img, src) { img.src = src; }
//加载图片代理函数 var loadImgProxy = (function(){ var imgCache = new Image(); return function(img, src){ imgCache.onload = function(){ loadImg(img, this.src); }; loadImg(img, 'loading.gif'); imgCache.src = src; }; })(); </script> </body> </html>