代理模式简述
代理是一个对象,跟本体对象具有相同的接口,以此达到对本体对象的访问控制。
简单言之,本体对象只注重业务逻辑的实现,代理则控制本体对象的实例化(何时实例化、何时被使用)。
代理模式的优点在于:代理对象可以代替本体对象被实例化,此时本体对象未真正实例化,等到合适时机再实例化。
代理模式可以延迟创建开销很大的本体对象,他会把本体的实例化推迟到有方法被调用时。
一个简单的例子
// 声明女孩对象 var girl = function (name) { this.name = name; }; // 声明男孩对象 var boy = function (girl) { this.girl = girl; this.sendGift = function (gift) { alert("Hi " + girl.name + ", 男孩送你一个礼物:" + gift); } }; // 声明代理对象 var proxyObj = function (girl) { this.girl = girl; this.sendGift = function (gift) { (new boy(girl)).sendGift(gift); // 替dudu送花咯 } }; var proxy = new proxyObj(new girl("花花")); proxy.sendGift("999朵玫瑰");
如上代码,girl是一个被送礼物的对象,boy是送礼物的对象,他保存了girl这个属性,还有一个送礼物的方法sendGift,然后他通过proxyObj去完成这件事,proxyObj就是代理,他把boy的礼物送给了girl,因此proxyObj同样需要保存girl的属性,同时也有sendGift方法,该方法实例化本体对象boy并调用了boy的sendGift方法,完成了boy送girl礼物的这个过程。
实战一:图片加载
在前端开发中,使用图片是非常常见的场景,如果直接给img标签设置src属性,如果图片过大,或网速比较慢,图片在加载过程中会有一段时间的空白,用户体验不好。
传统的解决方案
传统的解决方法是:在图片未加载完成之前,使用一个loading图标作为占位符,等图片完成加载后,再使用真实的图片地址替代loading图标。如下:
// 不使用代理的预加载图片函数如下 var myImage = (function(){ var imgNode = document.createElement("img"); document.body.appendChild(imgNode); var img = new Image(); img.onload = function(){ imgNode.src = this.src; }; return { setSrc: function(src) { imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif"; img.src = src; } } })(); // 调用方式 myImage.setSrc("https://www.baidu.com/img/bd_logo1.png");
如上代码,这是使用一般的编码方式实现图片的预加载技术的方案,首先创建imgNode元素,然后调用myImage.setSrc该方法的时候,先给图片一个预加载图片,当图片加载完的时候,再给img元素赋值。
这种方案是可以实现功能,但也有比较明显的缺陷:耦合性太高,myImage函数违背了面向对象设计原则中的单一职责原则,同时完成了创建img,设置loading加载状态等多个任务。
此时就可以使用代理模式来实现~~~
代理模式解决方案
var myImage = (function(){ var imgNode = document.createElement("img"); document.body.appendChild(imgNode); return { setSrc: function(src) { imgNode.src = src; } } })(); // 代理模式 var ProxyImage = (function(){ var img = new Image(); img.onload = function(){ myImage.setSrc(this.src); }; return { setSrc: function(src) { myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif"); img.src = src; } } })(); // 调用方式 ProxyImage.setSrc("https://www.baidu.com/img/bd_logo1.png");
如上代码,myImage 函数只负责做一件事,创建img元素加入到页面中,其中的加载loading图片交给代理函数ProxyImage 去做,当图片加载成功后,代理函数ProxyImage 会通知及执行myImage 函数的方法,同时当以后不需要代理对象的话,我们直接可以调用本体对象的方法即可。
代理模式跟本体对象具有相同的对外接口,有两个好处:
一、用户可以放心地请求代理,不需要了解代理的实现过程,只要结果符合预期即可。如果不需要代理对象了,可以换成调用本体对象的该方法
二、在任何使用本体对象的地方,都可以使用代理替换。
最后,强调一点,主体对象跟代理对象也可以都返回一个匿名函数,这样也认为他们具有相同的接口。
实战二:缓存代理
对第一次运行的结果进行缓存,当再一次运行相同运算的时候,直接从缓存里面取,避免重复运算,如果运算非常复杂的话,对性能很耗费,那么使用缓存对象可以提高性能。以下是一个简单的例子:
var mult = function(){ var a = 1; for(var i = 0,ilen = arguments.length; i < ilen; i+=1) { a = a*arguments[i]; } return a; }; // 计算加法 var plus = function(){ var a = 0; for(var i = 0,ilen = arguments.length; i < ilen; i+=1) { a += arguments[i]; } return a; } // 代理函数 var proxyFunc = function(fn) { var cache = {}; // 缓存对象 return function(){ var args = Array.prototype.join.call(arguments,','); if(args in cache) { return cache[args]; // 使用缓存代理 } return cache[args] = fn.apply(this,arguments); } }; var proxyMult = proxyFunc(mult); console.log(proxyMult(1,2,3,4)); // 24 console.log(proxyMult(1,2,3,4)); // 缓存取 24 var proxyPlus = proxyFunc(plus); console.log(proxyPlus(1,2,3,4)); // 10 console.log(proxyPlus(1,2,3,4)); // 缓存取 10
如上代码就是网上常见的加法和乘法的运算。通过缓存代理,可以减少不必要的运算量。
作者:5d18ee6b5b1c
链接:https://www.jianshu.com/p/46b0756c92a0
来源:简书
来源:https://www.cnblogs.com/chendahao/p/12218070.html