资讯专栏INFORMATION COLUMN

js设计模式笔记 - 观察者模式

txgcwm / 2448人阅读

摘要:姓名小强正式上班时间前端大大强订阅了这个消息姓名大大强正式上班时间发布者发布消息前端小强姓名小强正式上班时间大大强姓名大大强正式上班时间通过添加了一个,我们实现了对职位的判断。

观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
事实上,只要你曾经在DOM节点上绑定过事件函数,那么你就曾经使用过观察者模式了!

document.body.addEventListener("click", function () {
    alert(2);
});

但是这只是对观察者模式最简单的使用,在很多场景下我们经常会实现一些自定义事件来满足我们的需求。

举个例子:

你去一家公司应聘,谈了一顿下来,hr跟你说:"好了,你回去等通知吧!"。
这个时候,1.你会问公司的电话,然后每天打过去问一遍结果
          2.把自己的手机号留给hr,然后等他给你打电话
          
相信很多时候呢,大家都是选择了后者。
万一你每天给hr打电话弄烦他了,或许他本来打算招你的,现在也不再打算再鸟你啦!

那么这个时候,hr就相当于一个发布者,而你就是一个订阅者啦!

好吧,大部分叫你回去等消息的就等于没救啦......
我还遇到过一个如果你没被录取,就连通知都不通知你的公司!

那么一个简单的观察者模式应该怎么实现呢?

要指定一个发布者;

给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;(这家公司很多人来应聘)

最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数;(你up or 你over)

var event = {};    //发布者(hr)
event.clietList = []; //发布者的缓存列表(应聘者列表)

event.listen = function(fn) { //增加订阅者函数
    this.clietList.push(fn);
};

event.trigger = function() { //发布消息函数
    for (var i = 0; i < this.clietList.length; i++) {
        var fn = this.clietList[i];
        fn.apply(this, arguments);
    }
};

event.listen(function(time) { //某人订阅了这个消息
    console.log("正式上班时间:" + time);
});

event.trigger("2016/10",yes); //发布消息
//输出 正式上班时间:2016/10  

到这里,我们已经实现了一个最简单的观察者模式了!

但是上面的函数其实存在一个问题,那就是发布者没办法选择自己要发布的消息类型!
比如这家公司同时在招php,web前端,如果使用上面的函数就没办法区分职位了!只能一次性把全部订阅者都发送一遍消息。
对上面的代码进行改写:

var event = {}; //发布者(hr)
event.clietList = []; //发布者的缓存列表(应聘者列表)

event.listen = function(key, fn) { //增加订阅者函数
    if (!this.clietList[key]) {
        this.clietList[key] = [];
    }
    this.clietList[key].push(fn);
};

event.trigger = function() { //发布消息函数
    var key = Array.prototype.shift.call(arguments),
        fns = this.clietList[key];
    for (var i = 0; i < fns.length; i++) {
        var fn = fns[i];
        fn.apply(this, arguments);
    }
};

event.listen("web前端", fn1 = function(time) { //小强订阅了这个消息。
    console.log("姓名:小强");
    console.log("正式上班时间:" + time);
});

event.listen("web前端", fn2 = function(time) { //大大强订阅了这个消息
    console.log("姓名:大大强");
    console.log("正式上班时间:" + time);
});

//发布者发布消息
event.trigger("web前端","小强", "2016/10"); //姓名:小强   正式上班时间:2016/10  
event.trigger("php","大大强", "2016/15"); //姓名:大大强   正式上班时间:2016/15   

通过添加了一个key,我们实现了对职位的判断。

有了订阅事件,我们怎么能少了取消订阅事件呢?

event.remove = function(key, fn) {
    var fns = this.clietList[key];
    if (!fns) {
        return false;
    }
    if (!fn) { //如果没有传入fn回调函数,直接取消key对应消息的所有订阅
        this.clietList[key] = [];
    } else {
        for (var i = 0; i < fns.length; i++) { //遍历回调函数列表
            var _fn = fns[i];
            if (_fn === fn) {
                fns.splice(i, 1); //删除订阅者的回调函数
            }
        }
    }
};
//这时候必须指定回调函数,否则无法在remove函数中进行对比删除。
event.listen("web前端", fn1 = function(time) { //小强订阅了这个消息。
    console.log("姓名:小强");
    console.log("正式上班时间:" + time);
});

event.listen("web前端", fn2 = function(time) { //大大强订阅了这个消息
    console.log("姓名:大大强");
    console.log("正式上班时间:" + time);
});

event.remove("web前端",fn1);

//发布者发布消息
event.trigger("web前端","2016/10");

//输出 姓名:大大强   正式上班时间:2016/10 

对上面代码进行改进,创建一个全局对象来实现观察者模式,
使用闭包实现私有变量,仅暴露必须的API给使用者:

var event = (function() {
    var clietList = []; //发布者的缓存列表(应聘者列表)

    var listen = function(key, fn) { //增加订阅者函数
        if (!this.clietList[key]) {
            this.clietList[key] = [];
        }
        this.clietList[key].push(fn);
    };

    var trigger = function() { //发布消息函数
        var key = Array.prototype.shift.call(arguments),
            fns = this.clietList[key];
        for (var i = 0; i < fns.length; i++) {
            var fn = fns[i];
            fn.apply(this, arguments);
        }
    };

    var remove = function(key, fn) {
        var fns = this.clietList[key];
        if (!fns) {
            return false;
        }
        if (!fn) { //如果没有传入fn回调函数,直接取消key对应消息的所有订阅
            this.clietList[key] = [];
        } else {
            for (var i = 0; i < fns.length; i++) { //遍历回调函数列表
                var _fn = fns[i];
                if (_fn === fn) {
                    fns.splice(i, 1); //删除订阅者的回调函数
                }
            }
        }
    };
    
    return{
        listen:listen,
        trigger:trigger,
        remove:remove
    }
})();

观察者模式进阶:

使用命名空间防止事件名冲突

实现先发布后订阅功能

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91025.html

相关文章

  • 前端学习笔记察者模式

    摘要:观察者模式也称发布订阅模式它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态举个生活比较常见常见的例子比如你去面试之后,面试官看你表现不错,最后会跟你要联系方式,以便之后可以联系你。 观察者模式也称发布-订阅模式,它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态 举个生活比较常见常见的例子,比如你去面试之后,...

    tommego 评论0 收藏0
  • Backbone.js学习笔记(一)

    摘要:它通过数据模型进行键值绑定及事件处理,通过模型集合器提供一套丰富的用于枚举功能,通过视图来进行事件处理及与现有的通过接口进行交互。 本人兼职前端付费技术顾问,如需帮助请加本人微信hawx1993或QQ345823102,非诚勿扰 1.为初学前端而不知道怎么做项目的你指导 2.指导并扎实你的JavaScript基础 3.帮你准备面试并提供相关指导性意见 4.为你的前端之路提供极具建设性的...

    FrancisSoung 评论0 收藏0
  • 笔记】你不知道的JS读书笔记——Promise

    摘要:写在前面这一章的顺序对于未接触过使用过的童鞋而言略抽象了,前边几章主要为了说明和之前的异步方式相比有什么优势和它能解决什么问题,后边才详解的设计和各种场景下如何使用。建议先了解和简单使用过后再阅读,效果更佳。 写在前面:Promise这一章的顺序对于未接触过使用过Promise的童鞋而言略抽象了,前边几章主要为了说明Promise和之前的异步方式相比有什么优势和它能解决什么问题,后边才...

    mumumu 评论0 收藏0
  • MVC、MVP 和 MVVM 对比笔记

    摘要:模型与视图解耦,编写单元测试更方便。切实的将模型绑定到了视图,这一责任在中被控制器提前持有了。视图和视图模型使用数据绑定和事件进行通信。触发器数据触发器允许我们进一步在视图状态变化后改变我们的对象属性。 MVC、MVP 和 MVVM 三个非常重要的架构模式 MVC (Model(模型)-View(视图)-Controller(控制器)) MVP (Model(模型)-View(视图)...

    paney129 评论0 收藏0

发表评论

0条评论

txgcwm

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<