JS 设计模式 -代理模式

代理模式简述

代理是一个对象,跟本体对象具有相同的接口,以此达到对本体对象的访问控制。

简单言之,本体对象只注重业务逻辑的实现,代理则控制本体对象的实例化(何时实例化、何时被使用)。

代理模式的优点在于:代理对象可以代替本体对象被实例化,此时本体对象未真正实例化,等到合适时机再实例化。

代理模式可以延迟创建开销很大的本体对象,他会把本体的实例化推迟到有方法被调用时。

一个简单的例子

// 声明女孩对象
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
来源:简书

posted @ 2020-01-20 15:05  陈大浩爱学习  阅读(194)  评论(0编辑  收藏  举报