JavaScript之观察者模式

观察者模式也叫发布订阅模式!,为了解决主体对象与观察者之间功能的耦合。
定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖对象都会收到通知自动更新。

观察者模式主要分为三个步骤:注册、发布、移除。我们一个一个步骤来实现。
我们定义一个容器,再用闭包包起来,防止污染全局变量,里面存放这些方法,以及要观察的事件。

详细解释看注解

 //防止信息队列暴露而被慕改或影响到其他变量
 var Observer = (function () {
     var _messages = {};
     return {
         //注册信息接口
         regist: function (type, fn) {
             //如果此信息不存在则应该创建一个该消息类型
             if (typeof _messages[type] === 'undefined') {
                 //将动作推入到该信息对应的动作执行队列中
                 _messages[type] = [fn];

             } else {
                 _messages[type].push(fn);
             }
         },
         //发布信息接口
         fire: function (type, args) {
             //如果该小心没有被注册,则返回
             if (!_messages[type])
                 return;
             //定义小心信息
             var events = {
                     type: type, //消息类型
                     args: args || {} //消息携带数据
                 },
                 i = 0, //消息动作循环变量
                 len = _messages[type].length; //消息动作长度
             //遍历消息动作
             for (; i < len; i++) {
                 //依次执行注册的消息对应的动作序列
                 _messages[type][i].call(this, events);
             }
         },
         //移除信息接口
         remove: function (type, fn) {
             //如果消息动作队列存在
             if (_message[type] instanceof Array) {
                 //从最后一个消息动作遍历
                 var i = _message[type].length - 1;
                 for (; i >= 0; i--) {
                     //如果存在该动作在消息动作序列中移除相应动作
                     _messages[type][i] === fn && _messages[type].splice(i, 1);
                 }
             }
         }
     }
 })();

上面就是我们定义好的观察者,当我们想要订阅发布某些实例的时候直接调用即可。
接着我们结合实例来看一下这是怎么使用的,例如我们做一个留言并统计浏览数量的小功能

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div>
        <div>
            用户的数量
            <span id="msg_num">0</span>
        </div>
        <ul id="msg">

        </ul>
        <input type="text" id="user_input" />
        <button id="user_submit">提交</button>

    </div>




    <script>
        //防止信息队列暴露而被慕改或影响到其他变量
        var Observer = (function () {
            var _messages = {};
            return {
                //注册信息接口
                regist: function (type, fn) {
                    //如果此信息不存在则应该创建一个该消息类型
                    if (typeof _messages[type] === 'undefined') {
                        //将动作推入到该信息对应的动作执行队列中
                        _messages[type] = [fn];

                    } else {
                        _messages[type].push(fn);
                    }
                },
                //发布信息接口
                fire: function (type, args) {
                    //如果该小心没有被注册,则返回
                    if (!_messages[type])
                        return;
                    //定义小心信息
                    var events = {
                            type: type, //消息类型
                            args: args || {} //消息携带数据
                        },
                        i = 0, //消息动作循环变量
                        len = _messages[type].length; //消息动作长度
                    //遍历消息动作
                    for (; i < len; i++) {
                        //依次执行注册的消息对应的动作序列
                        _messages[type][i].call(this, events);
                    }
                },
                //移除信息接口
                remove: function (type, fn) {
                    //如果消息动作队列存在
                    if (_message[type] instanceof Array) {
                        //从最后一个消息动作遍历
                        var i = _message[type].length - 1;
                        for (; i >= 0; i--) {
                            //如果存在该动作在消息动作序列中移除相应动作
                            _messages[type][i] === fn && _messages[type].splice(i, 1);
                        }
                    }
                }
            }
        })();


        function $(id) {
            return document.getElementById(id);
        }

        (function () {
            //增加一则消息
            function addMsgItem(e) {
                var text = e.args.text, //获取消息中用户添加的文本内容
                    ul = $('msg'),
                    li = document.createElement('li'),
                    span = document.createElement('span');
                span.innerHTML = " 关闭"

                li.innerHTML = text;

                span.onclick = function () { //移除留言
                    ul.removeChild(li);
                    Observer.fire('removeCommentMessage', {
                        num: -1
                    });
                }
                li.appendChild(span);
                ul.appendChild(li);
            }
            Observer.regist('addCommentMessage', addMsgItem); //注册添加评论信息
        })();

        (function () {
            function changeMsgNum(e) {
                var num = e.args.num;
                $('msg_num').innerHTML = parseInt($('msg_num').innerHTML) + num;
            }
            Observer.regist('addCommentMessage', changeMsgNum);
            Observer.regist('removeCommentMessage', changeMsgNum);
        })();

        (function () {
            $('user_submit').onclick = function () {

                var text = $('user_input');
                if (text.value === '') {
                    return;
                }
                Observer.fire('addCommentMessage', {
                    text: text.value,
                    num: 1
                });
                text.value = '';
            }
        })();
    </script>
</body>

</html>

在这个例子中可以看出我们将每个模块抽离了出来,互不关系,完美解决模块间耦合功能。
其使用什么广泛,适用于关联于某些行为场景,并触发一系列事件。例如用来发布消息推送、点击事件侦听等等。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值