听说,代理模式真的只是个搞代购的?

1,405

存在即合理

总有些东西是值得研究研究的,虽然我对设计模式也不是很懂,但是学习研究了一下还是觉得受益匪浅的。

我们学习每一个知识点的时候都希望能一下子就能领悟,能吸收成为自己所用的技能。当然这也因人而异了

所以无论怎样,在学习研究的道路上,我们都没有放弃过,不抛弃不放弃应该是前端er给自己的一个口号了,加油,不再啰嗦了,直接进入今天的主题吧

代理模式

知其然当然要知其所以然

首先我们要知道什么是代理模式?

  • 它是为一个对象提供一个代替品占位符,以便控制对它的访问

然后在生活当中更是无处不在的

  • 歌手的经纪人啊
  • 球星(C罗、梅西)的经纪人啊
  • 买化妆品的代购啊
  • 等等等等

其实容易理解的就是: 遇到什么事,都交给那个有解决能力的人就对了

歌手接受采访经纪人来安排,球星转会俱乐部和经纪人来谈,买国外的化妆品找代购来搞定

光说不练假把式,还是实际一点,一起来看看代理模式在开发中的一些应用吧!

实际应用中是这样的

代理实现图片预加载

大家都知道如果页面加载图片的话,图片资源如果在网络情况较差的时候,加载是需要一些时间的,而且img元素在直接加载时也会出现一段时间的空白,这对体验来说是不好的

所以我们就需要先用一个占位图(如loading图),等真正的图片资源请求完成后再替换掉

下面先来看看代码

let myImg = (function() {
    let img = document.createElement('img');
    document.body.appendChild(img);

    return {
        // 设置真正的图片资源
        setSrc(src) {
            img.src = src;
        }
    }
})();

// 代理对象proxyImg
let proxyImg = (function() {
    let img = new Image();
    img.onload = function() {
        // 当图片资源加载成功后
        // 再重新给img的src赋值
        myImg.setSrc(this.src);
    }

    return {
        setSrc(src) {
            // 先给img元素设置loading图占位
            myImg.setSrc('https://p.ssl.qhimg.com/t0103b3b9a54ba2c6f5.gif');
            img.src = src;
        }
    }
})();

proxyImg.setSrc('http://p0.so.qhimgs1.com/bdr/326__/t015b81c99692979474.jpg');

OK,通过上面的代码就实现了一个图片代理预加载的技术点。

当然了,有时候会发现,其实不用代理也完全可以实现啊,把img.onload的那些代码写到myImg的代码块中也是OK的啊,何必多此一举,又造了一个代理函数呢?

  • 主要是代理函数它的作用是用来预加载处理的,如果以后网速快如闪电的话,那还需要预加载吗?
  • 答案肯定是不需要了,那我们就还要回到myImg的代码中去找img.onload的操作,然后再根据无用的代码进行删除(万一删错了怎么办,会出事故的)
  • 但用到了代理函数就不需要这样了,不再调用即可了,按照下面这样写就好了
    myImg.setSrc('http://p0.so.qhimgs1.com/bdr/326__/t015b81c99692979474.jpg');
    

合并http请求

在web开发中,最大的开销莫过于就是网络请求了

例如在一些同步文件上传的情况下,如果是点击一个复选框就发送一次请求的话,这样多搞个几次,服务器的压力就会变大了,身为前端不能这么搞服务器的,不够仁义啊

那么应该如何是好呢?不要方,按照代理模式的开发思路应该就是,不用自己亲自上,交给代理来

实现的原理其实就是把点击到的要同步的文件先存起来,然后在一个合理的延迟时间后再统一发送这次的请求就好了(这一步放到代理中来做)

// 同步文件函数
function syncFile(id) {
    const url = 'xxx.com/index.php';
    axios.get(url, {
        params: {
            id
        }
    });
}
let proxySyncFile = (function() {
    // cache保存一段时间内需要同步的id
    let cache = [],     
        timer;

    return function(id) {
        cache.push(id);
        // 保证不会覆盖之前的定时器
        if (timer) return;

        timer = setTimeout(function() {
            // 向2秒后本体发送需要同步的id集合
            syncFile(cache.join(','));
            
            clearTimeout(timer);
            timer = null;
            cache.length = 0;  // 清空id集合
        }, 2000);
    }
})();

以上代码就完成了代理合并http请求的实现了,下面再看一下使用就万事大吉了

// 遍历所有的checkbox元素
let checkbox = document.getElementsByTagName('input');

for (let i = 0; i < checkbox.length; i++) {
    let c = checkbox[i];
    // 给每一个checkbox元素添加click事件
    c.onclick = function() {
        // 被选中的情况下再触发代理函数并传入对应的id
        if (this.checked === true) {
            proxySyncFile(this.id);
        }
    };
}

缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次计算时

如果传递进来的参数和之前一致,则可以直接返回前面存储的运算结果

下面以最常见的计算乘积的栗子来看一下吧

    // 计算乘积栗子
    function mult() {
        console.log('开始计算乘积');
        let a = 1;
        for (let i = 0, len = arguments.length; i < len; i++) {
            a = a * arguments[i];
        }
        return a;
    }
    // 缓存代理
    let proxyMult = (function() {
        // 缓存对象,用来存储计算结果用的
        let cache = {};
        
        return function() {
            const args = Array.prototype.join.call(arguments, ',');
            // 遍历cache对象,看是否有上一次计算的参数
            if (args in cache) {
                console.log('走了缓存');
                return cache[args];
            }
            return cache[args] = mult.apply(this, arguments);
        }
    })();
    
    console.log(proxyMult(2, 3));   // 开始计算乘积 6
    console.log(proxyMult(2, 3, 5, 10, 2)); // 开始计算乘积 600
    console.log(proxyMult(2, 3, 5, 10, 2)); // 走了缓存 600
    console.log(proxyMult(2, 10, 2));   // 开始计算乘积 40

利用缓存来把开销大的运算暂存起来确实是一种很有效的方法,下面我们再把这个缓存代理做的更通用一些

不管是计算乘积还是计算加和我们都可以通过改造后的缓存代理来实现,看代码

    function createProxy(fn) {
        let cache = {};
        return function() {
            const args = Array.prototype.join.call(arguments, ',');
            if (args in cache) {
                return cache[args];
            }
            return cache[args] = fn.apply(this, arguments);
        }
    }

以上代码唯一不同的地方就是把函数通过参数的形式传进去就可以针对不同的计算函数来进行缓存代理了,惟妙惟肖,确实是好方法,哈哈

总结

在开发中代理模式还是很重要的,值得我们多去写写,多去加以实践

以上内容并不是很多,相比之前写的篇幅大大缩减,但是初心不变,依然希望能给大家带来一些好的内容分享出来,哈哈,感谢大家的观看了!