专栏首页Super 前端JavaScript设计模式--观察者模式

JavaScript设计模式--观察者模式

一、定义

观察者模式(发布-订阅模式):其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。 在JavaScript中,一般使用事件模型来替代传统的观察者模式。 好处: (1)可广泛应用于异步编程中,是一种替代传递回调函数的方案。 (2)可取代对象之间硬编码的通知机制,一个对象不用再显示地调用另外一个对象的某个接口。两对象轻松解耦。

二、DOM事件–观察者模式典例

需要监控用户点击document.body的动作,但是我们没有办法预知用户将在什么时间点击。 所以,我们订阅document.body上的click事件,当body节点被点击时,body节点便向订阅者发布这个消息!

document.body.addEventListener("click", function() {
    console.log(1);
}, false);

// 可以多个订阅者
document.body.addEventListener("click", function() {
    console.log(2);
}, false);

doucment.body.click();

某网站有header头部、nav导航、消息列表等模块。这几个模块的渲染都需要获取用户登陆信息。 (1)一般写法:

$.ajax({
    url: './login',
    type: 'post',
    contentType: 'application/json',
    dataType:'json',
    success: function(data) {
        if(data.status === "success") {
            // 登录成功,渲染header、nav
            header.setInfo(data.headerInfo);
            nav.setInfo(data.navInfo);
        }
    }
});

(2)使用观察者模式,很轻松解耦!

$.ajax({
    ...,
    success: function(data) {
        if(data.status === "success") {
            // 登录成功,发布登陆成功消息
            login.trigger("loginsuccess", data);
        }
    }
});

var header = (function() {
    // 监听消息
    login.listen("loginsuccess", function(data){
        header.setInfo(data.headerInfo);
    });
    return {
        setInfo: function(data) {
            console.log("设置header信息");
        }
    };
})();

var nav = (function() {
    login.listen("loginsuccess", function(data){
        nav.setInfo(data.navInfo);
    });
    return {
        setInfo: function(data) {
            console.log("设置nav信息");
        }
    }
})();

三、通用观察者模式

/*
 * 示例:
 * Event.create("namespace1").listen('click', function(a){
 *      console.log(a);
 * });
 * Event.create("namespace1").trigger("click", 1);
 */
var Event = (function() {
   var global = this,
       Event,
       _default = 'default';

    Event = function() {
        var _listen,
            _trigger,
            _remove,
            _slice = Array.prototype.slice,
            _shift = Array.prototype.shift,
            _unshift = Array.prototype.unshift,
            namespaceCache = [],
            _create,
            find,
            each = function( ary, fn) {
                var ret;
                for(var i = 0, l = ary.length; i < l; i++) {
                    var n = ary[i];
                    ret = fn.call(n, i, n);
                }
                return ret;
            };
        // 订阅
        _listen = function(key, fn, cache) {
            if(!cache[key]) {
                cache[key] = [];
            }
            cache[key].push(fn);
        };
        // 移除订阅
        _remove = function(key, cache, fn) {
            if(cache[key]) {
                if(fn) {
                    for(var i = cache[key].length; i >=0; i++) {
                        if(cache[key][i] === fn) {
                            cache[key].splice(i, 1);
                        }
                    }
                }else {
                    cache[key] = [];
                }
            }
        };
        // 发布
        _trigger = function() {
          var cache = _shift.call(arguments),
              key = _shift.call(arguments),
              args = arguments,
              _self = this,
              ret,
              stack = cache[key];
            if(!stack || !stack.length) {
                return;
            }
            return each(stack, function() {
               return this.apply(_self, args);
            });
        };
        // 创建命名空间
        _create = function(namespace) {
            var namespace = namespace || _default;
            var cache = {},
                offlineStack = [],  // 离线事件
                ret = {
                    listen: function (key, fn, last) {
                        _listen(key, fn, cache);
                        if (offlineStack == null) {
                            return;
                        }
                        if (last === 'last') {
                            offlineStack.length && offlineStack.pop()();
                        } else {
                            each(offlineStack, function () {
                                this();
                            });
                        }
                        offlineStack = null;
                    },

                    one: function (key, fn, last) {
                        _remove(key, cache);
                        this.listen(key, fn, last);
                    },

                    remove: function(key, fn, last) {
                        _remove(key, cache, fn);
                    },

                    trigger: function() {
                        var fn,
                            args,
                            _self = this;
                        _unshift.call(arguments, cache);
                        args = arguments;
                        fn = function() {
                            return _trigger.apply(_self, args);
                        };
                        if(offlineStack) {
                            return offlineStack.push(fn);
                        }
                        return fn;
                    }
                };

                return namespace ? (namespaceCache[namespace] ? namespaceCache[namespace] : namespaceCache[namespace] = ret) : ret;
            };

        return {
            create: _create,
            one: function(key, fn, last) {
                var event = this.create();
                event.one(key, fn, last);
            },
            remove: function(key, fn) {
                var event = this.create();
                event.remove(key, fn);
            },
            listen: function(key, fn, last) {
                var event = this.create();
                event.listen(key, fn, last);
            },
            trigger: function() {
                var event = this.create();
                event.trigger.apply(this, arguments);
            }
        };
    }();
    return Event;
})();

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaScript设计模式--代理模式

    代理模式:为一个对象提供一个代用品或占位符,以便控制对它的访问。 代理分为:保护代理和虚拟代理 保护代理:用于控制不同权限的对象对目标对象的访问,在Java...

    奋飛
  • JavaScript异步编程设计快速响应的网络应用

    因为setTimeout回调在while循环结束运行之前不可能被触发! 调用setTimeout时,会有一个延时事件排入队列。然后继续执行下一行代码,直到再...

    奋飛
  • JavaScript作用域闭包(你不知道的JavaScript)

    JavaScript闭包,是JS开发工程师必须深入了解的知识。3月份自己曾撰写博客《JavaScript闭包》,博客中只是简单阐述了闭包的工作过程和列举了几个示...

    奋飛
  • Functions在JavaScript中是作为 first class objects存在的

    Functions在JavaScript中是作为 'first class objects' 存在的。这意味着JS中的functions是一种特殊类型的obje...

    lesM10
  • JavaScript之使用JavaScript模仿oop编程

    wfaceboss
  • webRTC 初探

    webRTC是英文Web Real-Time Communication的缩写,中文翻译网页实时通信,是浏览器不需要服务器的中转,可以直接通信的技术

    frontoldman
  • Git 如何压缩 commit

    今天同事突然问我,由于在给老大的开源项目提 pr 的时候,自己比较长时间没有 rebase 的老大的项目 master 分支了,而自己提交的 commit 又很...

    runzhliu
  • 一篇文章学会使用 gitk,排查 Git 问题就靠它了

    现在软件开发已经离不开版本控制系统。版本控制系统不仅能保留每个文件所有版本的历史信息,还能够方便查找。另外,也能够让团队协作不受空间和时间的限制。

    DevOps时代
  • 这才是真正的Git——Git实用技巧

    ? 作者:lzaneli,腾讯 TEG 前端开发工程师 本篇是这个系列的最后一篇文章,之前的文章主要讲的是基础原理部分(见上方专辑),在理解原理的基础上,介绍...

    腾讯技术工程官方号
  • git 将多个commit id 打包成一个或者是多个commit id

    我们有时候会出现频繁提交代码的问题,可能多个commit id 实际上一个作用,为了让git log看起来更清晰,我们会将多个commit id 合并成若干个c...

    张凝可

扫码关注云+社区

领取腾讯云代金券