代理模式

代理模式

代理模式和图片预加载

先不使用代理模式做一个图片预加载功能。

var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    var img = new Image;
    img.onload = function(){
        imgNode.src = img.src;
    }
    return {
        setSrc:function(src){
            imgNode.src = "./bd_logo1.png"
            img.src = src
        }
    }
})()
myImage.setSrc("http://data.17jita.com/attachment/forum/201412/20/213333npk8mvppcav3rmv8.png")

问题是,当我们网速不再需要预加载,那就不得不改动 myImage 对象。

同时,myImage 除了负责给 img 节点设置 src,还要负责预加载图片,违背了单一职责原则。

下面使用代理模式实现预加载功能

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("./bd_logo1.png")
            img.src = src;
        }
    }
})()
proxyImage.setSrc("http://data.17jita.com/attachment/forum/201412/20/213333npk8mvppcav3rmv8.png")

通过 proxyImage 间接访问 myIname,控制图片的预加载。

当不需要预加载的时候,只用把对 proxyImage 的引用改成 myImage 即可。

虚拟代理合并 http 请求

在 web 开发中,也需最大的开销就是网络请求,假设我们在做一个文件同步功能,在我们选中一个 checkbox 的时候,它对应的文件就会被同步到另一个服务器上

var synchronousFile = function(id){
    console.log("开始同步文件"+id)
}
var proxysynchronousFile = (function(){
    var cache = [],
    timer;
    return function(id){
        cache.push(id);
        if(timer){
            retrurn
        };
        timer = setTimeout(function(){
            synchronousFile(cache.join(","))
            clearTimeout(timer);
            timer = null;
            cache.length = 0;
        },2000)
    }
})()
var checkbox = document.getElementByTagName("input");
for(var i=0,c;c = checkbox[i];i++){
    c.onclick = function(){
        if(this.checked===true){
            proxysynchronousFile(this.id)
        }
    }
}

缓存代理

缓存代理的应用-计算乘积

缓存代理可以为运算结果提供暂时的存储,在下次运算时,如果传毒的参数和之前一致,则可以直接返回前面储存的计算结果

var mult = function(...numbers){//es6  rest参数
    var a= 1;
    for(var i=0,number;number = numbers[i];i++){
        a*=number
    }
    return a
}
mult(2,3)//6

加入缓存代理

var proxyMult = (function(){
    var chache = {};
    return function(...numbers){
        var args = numbers.join(',')
        if(chache[args]){
            return chache[args]
        }
        return chache[args] = mult(...numbers)//es6函数拓展运算符,rest参数逆运算
    }
})(
)
proxyMult(2,3)//6
缓存代理的应用-ajax 异步请求资源

在项目中遇到的分页需求,同一页面理论上只需要后台取得一次,下次在请求同一页时,可以直接使用之前的缓存
也可以引用缓存代理,和计算乘积不同的地方在于请求数据是异步操作无法直接把结果放入缓存中,要使用异步的回调。

var chache = {};
var request = function(num){
    ajax(num)//假装是个ajax请求数据的操作
    .success(function(res){
        render(res.data)//假装是个渲染操作
        chache[num] = res.data
    })
}
var getPage = function(num){
    request(num)
}

var proxyPage = function(){
    return function(num){
        if(chache[num]){
            return chache[num]
        }
        getPage(num)
    }
}

用高阶函数动态创建代理

可以将计算函数作为参数传入创建缓存的代理中,这样就可以为乘法加减法统一创建代理

//乘法
var mult = (...args)=>{
    var a = 1 ;
    for(var i=0,c;c=args[i];i++){
        a *=c
    }
    return a
}
//加法
var plus = (...args)=>{
    var a = 0;
    for(var i=0,c;c=args[i];i++){
        a+=c
    }
    return a
}

//代理工厂
var createProxyFactory = (fn)=>{
    var chache = {};
    return function(...args){
        var arg = args.join(",");
        if(arg in chache){
            return chache[arg]
        }
        return chache[arg] = fn(...args)
    }
};
var proxyMult = createProxyFactory(mult),
proxyPlus = createProxyFactory(plus);

proxyMult(1,2,3,4)//24
proxyPlus(1,2,3,4)//10

小结

代理模式的关键是,当不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象控制对这个对象的访问。替身对象对请求做出一些处理后,再转交给本体

在 javascript 开发中最常用的代理模式是虚拟代理和缓存代理。

虚拟代理把开销大的对象延迟到真正需要的时候创建。(imgNode 在 img onload 结束之后再设置其 src)

缓存代理为一些开销大的运算结果提供暂时的存储。(将结果用 chache 存储)

编写业务代码的时候,往往不需要去预先猜测是否需要使用,当不方便直接访问某个对象的时候,再编写代理不迟