下面代码是图片预加载的一个简单实现, 先不考虑加载图片时 onError , onAbort , 超时等问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <!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="../script/jquery-1.9.1.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, 'https://i.postimg.cc/tRMNsqnJ/Yukee4.png'); }
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) { var imgCache = new Image(); imgCache.onload = function(){ img.src = this.src; };
img.src = 'loading.gif'; imgCache.src = src; } </script> </body>
</html>
|
上述代码在功能上实现了图片预加载,但是它包含了预加载和加载两项职责,违反了 “单一职责原则”。所谓的职责就是“会发生的变化”,如果网速不再是问题或者加载图片的分辨率被控制在很小的时候等,需要去掉预加载的功能,这时候就要修改loadImg的代码,就要重新跑所有相关的测试,即违反了 “开闭原则”,又增加测试工作。
设计模式有如下原则:
- 开闭原则:对扩展开放,对修改关闭
- 里氏转换原则:子类继承父类,单独调用完全可以运行
- 依赖倒转原则:引用一个对象,如果这个对象有底层类型,直接引用底层
- 接口隔离原则:每个接口应该是一个角色
- 合成/聚合复用原则:新对象应该使用一些已有的对象,使之成为新对象的一部分
- 迪米特原则:一个对象应该对其它对象有尽可能少的了解
加载和预加载其实就是代理模式的一种,代理模式可以理解为你想给MM送东西,但是不知道MM喜欢什么,就找她的闺蜜帮你买她喜欢的东西交给你,然后你再送给她。
将预加载功能改为代理模式可以理解为:本体先显示个门面,让代理去帮忙加载大图,加载完了,告诉本体,本体直接把图片贴上去就行了。
加载本体函数
1 2 3
| function loadImg(img, src) { img.src = src; }
|
写一个预加载代理函数
1 2 3 4 5 6 7 8 9
| 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对象变为私有,不必每次都new一个新的image对象,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12
| 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; }; })();
|
小结
- 通过代理对象,添加了新的行为,符合
开放-封闭原则
- 图片预加载和给 img 设置 src 这两个功能被分隔到两个方法中,它们各自变化不影响另外一个
- 如果以后不需要预加载了,只需要修改函数名即可
参考文章