前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >深入理解JavaScript系列(31):设计模式之代理模式

深入理解JavaScript系列(31):设计模式之代理模式

作者头像
用户4962466
修改于 2020-01-16 02:13:23
修改于 2020-01-16 02:13:23
40400
代码可运行
举报
文章被收录于专栏:我的前端路我的前端路
运行总次数:0
代码可运行

代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下:

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。

代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西。

正文

我们来举一个简单的例子,假如dudu要送酸奶小妹玫瑰花,却不知道她的联系方式或者不好意思,想委托大叔去送这些玫瑰,那大叔就是个代理(其实挺好的,可以扣几朵给媳妇),那我们如何来做呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 先声明美女对象
var girl = function (name) {
    this.name = name;
};

// 这是dudu
var dudu = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        alert("Hi " + girl.name + ", dudu送你一个礼物:" + gift);
    }
};

// 大叔是代理
var proxyTom = function (girl) {
    this.girl = girl;
    this.sendGift = function (gift) {
        (new dudu(girl)).sendGift(gift); // 替dudu送花咯
    }
};

复制代码

调用方式就非常简单了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var proxy = new proxyTom(new girl("酸奶小妹"));
proxy.sendGift("999朵玫瑰");

复制代码

实战一把

通过上面的代码,相信大家对代理模式已经非常清楚了,我们来实战下:我们有一个简单的播放列表,需要在点击单个连接(或者全选)的时候在该连接下方显示视频曲介绍以及play按钮,点击play按钮的时候播放视频,列表结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<h1>Dave Matthews vids</h1>
<p><span id="toggle-all">全选/反选</span></p>
<ol id="vids">
  <li><input type="checkbox" checked><a href="http://www.caishui114.com/gongsi/">Gravedigger</a></li>
  <li><input type="checkbox" checked><a href="http://www.caishui114.com/dailijz/">Save Me</a></li>
  <li><input type="checkbox" checked><a href="http://www.caishui114.com/news/">Crush</a></li>
  <li><input type="checkbox" checked><a href="http://www.caishui114.com/cszt/">Don't Drink The Water</a></li>
  <li><input type="checkbox" checked><a href="http://www.caishui114.com/chanpin/">Funny the Way It Is</a></li>
  <li><input type="checkbox" checked><a href="http://www.caishui114.com/wentiku/">What Would You Say</a>
</li>
</ol>

复制代码运行代码

我们先来分析如下,首先我们不仅要监控a连接的点击事件,还要监控“全选/反选”的点击事件,然后请求服务器查询视频信息,组装HTML信息显示在li元素的最后位置上,效果如下:

然后再监控play连接的点击事件,点击以后开始播放,效果如下:

好了,开始,没有jQuery,我们自定义一个选择器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var $ = function (id) {
    return document.getElementById(id);
};

复制代码

由于Yahoo的json服务提供了callback参数,所以我们传入我们自定义的callback以便来接受数据,具体查询字符串拼装代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var http = {
    makeRequest: function (ids, callback) {
        var url = 'http://www.0755dyx.com/',
            sql = 'select * from music.video.id where ids IN ("%ID%")',
            format = "format=json",
            handler = "callback=" + callback,
            script = document.createElement('script');

            sql = sql.replace('%ID%', ids.join('","'));
            sql = encodeURIComponent(sql);

            url += sql + '&' + format + '&' + handler;
            script.src = url;

        document.body.appendChild(script);
    }
};

复制代码

代理对象如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var proxy = {
    ids: [],
    delay: 50,
    timeout: null,
    callback: null,
    context: null,
    // 设置请求的id和callback以便在播放的时候触发回调
    makeRequest: function (id, callback, context) {

        // 添加到队列dd to the queue
        this.ids.push(id);

        this.callback = callback;
        this.context = context;

        // 设置timeout
        if (!this.timeout) {
            this.timeout = setTimeout(function () {
                proxy.flush();
            }, this.delay);
        }
    },
    // 触发请求,使用代理职责调用了http.makeRequest
    flush: function () {
        // proxy.handler为请求yahoo时的callback
        http.makeRequest(this.ids, 'proxy.handler'); 
        // 请求数据以后,紧接着执行proxy.handler方法(里面有另一个设置的callback)

        // 清楚timeout和队列
        this.timeout = null;
        this.ids = [];

    },
    handler: function (data) {
        var i, max;

        // 单个视频的callback调用
        if (parseInt(data.query.count, 10) === 1) {
            proxy.callback.call(proxy.context, data.query.results.Video);
            return;
        }

        // 多个视频的callback调用
        for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {
            proxy.callback.call(proxy.context, data.query.results.Video[i]);
        }
    }
};

复制代码

视频处理模块主要有3种子功能:获取信息、展示信息、播放视频:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var videos = {
    // 初始化播放器代码,开始播放
    getPlayer: function (id) {
        return '' +
            '<object width="400" height="255" id="uvp_fop" allowFullScreen="true">' +
            '<param name="movie" value="http://d.yimg.com/m/up/fop/embedflv/swf/fop.swf"\/>' +
            '<param name="flashVars" value="id=v' + id + '&amp;eID=1301797&amp;lang=us&amp;enableFullScreen=0&amp;shareEnable=1"\/>' +
            '<param name="wmode" value="transparent"\/>' +
            '<embed ' +
            'height="255" ' +
            'width="400" ' +
            'id="uvp_fop" ' +
            'allowFullScreen="true" ' +
            'src="http://d.yimg.com/m/up/fop/embedflv/swf/fop.swf" ' +
            'type="application/x-shockwave-flash" ' +
            'flashvars="id=v' + id + '&amp;eID=1301797&amp;lang=us&amp;ympsc=4195329&amp;enableFullScreen=1&amp;shareEnable=1"' +
            '\/>' +
            '<\/object>';
                },
    // 拼接信息显示内容,然后在append到li的底部里显示
    updateList: function (data) {
        var id,
            html = '',
            info;

        if (data.query) {
            data = data.query.results.Video;
        }
        id = data.id;
        html += '<img src="' + data.Image[0].url + '" width="50" \/>';
        html += '<h2>' + data.title + '<\/h2>';
        html += '<p>' + data.copyrightYear + ', ' + data.label + '<\/p>';
        if (data.Album) {
            html += '<p>Album: ' + data.Album.Release.title + ', ' + data.Album.Release.releaseYear + '<br \/>';
        }
        html += '<p><a class="play" href="http://new.music.yahoo.com/videos/--' + id + '">&raquo; play<\/a><\/p>';
        info = document.createElement('div');
        info.id = "info" + id;
        info.innerHTML = html;
        $('v' + id).appendChild(info);
    },
    // 获取信息并显示
    getInfo: function (id) {
        var info = $('info' + id);

        if (!info) {
            proxy.makeRequest(id, videos.updateList, videos); //执行代理职责,并传入videos.updateList回调函数
            return;
        }

        if (info.style.display === "none") {
            info.style.display = '';
        } else {
            info.style.display = 'none';
        }
    }
};

复制代码

现在可以处理点击事件的代码了,由于有很多a连接,如果每个连接都绑定事件的话,显然性能会有问题,所以我们将事件绑定在

元素上,然后检测点击的是否是a连接,如果是说明我们点击的是视频地址,然后就可以播放了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$('vids').onclick = function (e) {
    var src, id;

    e = e || window.event;
    src = e.target || e.srcElement;

    // 不是连接的话就不继续处理了
    if (src.nodeName.toUpperCase() !== "A") {
        return;
    }
    //停止冒泡
    if (typeof e.preventDefault === "function") {
        e.preventDefault();
    }
    e.returnValue = false;

    id = src.href.split('--')[1];

    //如果点击的是已经生产的视频信息区域的连接play,就开始播放
    // 然后return不继续了
    if (src.className === "play") {
        src.parentNode.innerHTML = videos.getPlayer(id);
        return;
    }

    src.parentNode.id = "v" + id;
    videos.getInfo(id); // 这个才是第一次点击的时候显示视频信息的处理代码
};

复制代码

全选反选的代码大同小异,我们就不解释了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$('toggle-all').onclick = function (e) {

    var hrefs, i, max, id;

    hrefs = $('vids').getElementsByTagName('a');
    for (i = 0, max = hrefs.length; i < max; i += 1) {
        // 忽略play连接
        if (hrefs[i].className === "play") {
            continue;
        }
        // 忽略没有选择的项
        if (!hrefs[i].parentNode.firstChild.checked) {
            continue;
        }

        id = hrefs[i].href.split('--')[1];
        hrefs[i].parentNode.id = "v" + id;
        videos.getInfo(id);
    }
};

总结

代理模式一般适用于如下场合:

  • 远程代理,也就是为了一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实,就像web service里的代理类一样。
  • 虚拟代理,根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,比如浏览器的渲染的时候先显示问题,而图片可以慢慢显示(就是通过虚拟代理代替了真实的图片,此时虚拟代理保存了真实图片的路径和尺寸。
  • 安全代理,用来控制真实对象访问时的权限,一般用于对象应该有不同的访问权限。
  • 智能指引,只当调用真实的对象时,代理处理另外一些事情。例如C#里的垃圾回收,使用对象的时候会有引用次数,如果对象没有引用了,GC就可以回收它了。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《JavaScript 模式》读书笔记(7)— 设计模式3
这一篇,我们学习本篇中最为复杂的三个设计模式,代理模式、中介者模式以及观察者模式。这三个模式很重要!!
zaking
2020/09/03
6370
深入理解JavaScript系列(44):设计模式之桥接模式
上述代码,有个问题就是getBeerById必须要有浏览器的上下文才能使用,因为其内部使用了this.id这个属性,如果没用上下文,那就歇菜了。所以说一般稍微有经验的程序员都会将程序改造成如下形式:
用户4962466
2019/12/19
4180
深入理解JavaScript系列(49):Function模式(上篇)
本篇主要是介绍Function方面使用的一些技巧(上篇),利用Function特性可以编写出很多非常有意思的代码,本篇主要包括:回调模式、配置对象、返回函数、分布程序、柯里化(Currying)。
用户4962466
2019/12/16
3580
「设计模式 JavaScript 描述」代理模式
代理模式是一种非常有意义的模式,在生活中可以找到很多代理模式的场景。比如,明星都有经纪人作为代理。如果想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演出的细节和报酬都谈好之后,再把合同交给明星签。
用户8921923
2022/10/24
3720
学习《JavaScript设计模式与开发实践》- 代理模式
最近在学习《JavaScript设计模式与开发实践》一书,每看完一个模块,会写一篇文档来做为笔记,如果你也在学习JS设计模式,欢迎留言交流。
Jou
2022/08/10
2550
JavaScript设计模式--代理模式
代理模式:为一个对象提供一个代用品或占位符,以便控制对它的访问。 代理分为:保护代理和虚拟代理 保护代理:用于控制不同权限的对象对目标对象的访问,在JavaScript中很难判断谁访问了某个对象,所以保护代理很难实现。
奋飛
2019/08/15
4350
深入理解JavaScript系列(50):Function模式(下篇)
本篇我们介绍的一些模式称为初始化模式和性能模式,主要是用在初始化以及提高性能方面,一些模式之前已经提到过,这里只是做一下总结。
用户4962466
2019/12/16
2990
深入理解JavaScript系列(36):设计模式之中介者模式
中介者模式(Mediator),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
用户4962466
2020/01/02
3860
深入理解JavaScript系列(37):设计模式之享元模式
享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。
用户4962466
2020/01/02
4550
JavaScript设计模式 代理模式
代理模式的关建是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际访问的是替身对象,替身对象对请求做出一些处理后,再把请求转交给本体对象。
菜的黑人牙膏
2019/01/21
3470
深入理解JavaScript系列(28):设计模式之工厂模式
与创建型模式类似,工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类。
用户4962466
2020/01/16
3080
javascript设计模式三:代理模式
单一职责其实就是指在一个类中(js中通常指对象和函数等),应仅有一个引起它变化的原因。这样会帮助程序设计具有良好的健壮和高内聚特性,从而当变化发生时,程序设计会尽量少的受到意外破坏。
前端_AWhile
2019/08/29
3070
深入理解JavaScript系列(32):设计模式之观察者模式
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
用户4962466
2020/01/07
4500
JavaScript设计模式之代理模式
https://github.com/ahwgs/design-pattern-learning/tree/master/7.JavaScript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F
w候人兮猗
2020/07/01
2830
深入理解JavaScript系列(35):设计模式之迭代器模式
迭代器模式(Iterator):提供一种方法顺序一个聚合对象中各个元素,而又不暴露该对象内部表示。
用户4962466
2020/01/07
3360
深入理解JavaScript系列(45):代码复用模式(避免篇)
任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免使用这些模式,因为或多或少有带来一些问题;第二排是推荐篇,指的是推荐大家使用的模式,一般不会有什么问题。
用户4962466
2019/12/19
3090
Javascript快速入门(下篇)
Javascript, cheer up。 Ajax:其通过在Web页面与服务器之间建立一个额外的处理层,这个处理层就被称为Ajax引擎,它解释来自用户的请求,在后台以异步的方式处理服务器通信,其结
用户1216676
2018/01/24
9460
Javascript快速入门(下篇)
JavaScript设计模式之代理模式
当你想买商业保险的时候,却不得不亲自去了解不同公司的方案和限制。在自己百忙之中分神和不同的销售博弈。有了职业的保险经纪人,你只要向他/她讲述你的需求,经纪人就用自己的关系量身定做了一套方案给你。买哪家,怎么买,等等。保险经纪代理的模式,很好地体现了程序设计关注点分离的思想。
一粒小麦
2019/10/30
3390
JavaScript设计模式之代理模式
深入设计模式-代理模式
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
chenchenchen
2020/05/26
8060
javascript设计模式-代理模式
代理模式在javascript中可以使用Proxy对象,可以更好的去控制一些对象的交互,既然谈到了Proxy,我们先简单了解一下Proxy到底是干什么的
FE情报局
2022/12/05
3260
javascript设计模式-代理模式
推荐阅读
相关推荐
《JavaScript 模式》读书笔记(7)— 设计模式3
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文