js的发布-订阅者者模式

619 阅读5分钟

ps:这模式了解一下就行,我也没过多的写,因为代码太多了。。累死个人,代码有点多,我写了一些注释,大家慢慢看

含义说明

发布-订阅模式又叫观察者模式,它定义对象间的一种对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知先订阅再发布

作用

1.支持简单的广播通信,当对象状态发生改变时,会自动通知已经订阅过的对象。

2.可以应用在异步编程中替代回调函数可以订阅aj ax之后的事件只需要订阅自己需要的部分(那么ajax调用发布之后订阅的就可以拿到消息了) (不需要关心对象在异步运行时候的状态)

3.对象之间的松耦合两个对象之间都互相不了解彼此但是不影响通信当有新的订阅者出现的时候发布的代码无需要改变同样发布的代码改变只要之前约定的事件的名称没有改变也不影响订阅

4.vue和react之间实现跨组件之间的传值

生活中的实例

比如小红最近在淘宝网上看上一双鞋子,但是呢联系到卖家后,才发现这双鞋卖光了,但是小红对这双鞋又非常 喜欢,所以呢联系卖家,问卖家什么时候有货,卖家告诉她,要等个星期后才有货, 卖家告诉小红,要是你喜欢 的话,你可以收藏我们的店铺,等有货的时候再通知你,所以小红收藏了此店铺,但与此同时,小明,小花等也喜 欢这双鞋,也收藏了该店铺;等来货的时候就依次会通知他们:

如何实现发布一订阅模式?

-1. 首先要想好谁是发布者(比如上面的卖家)。

-2. 然后给发布者添加一个缓存列表,用于存放回调函数来通知订阅(比如上面的买家收藏了卖家的店铺,卖家通过收藏了该店铺的一个列表名单)。

-3.最后就是发布消息,发布者遍历这个缓存列表,依次触发里面存放的订阅者回调函数。

 var shopObj = {} //定义发布者
        shopObj.list = [] //缓存列表,存放订阅的函数
        //    增加订阅者
        shopObj.listen = function (key, fn) {
            if (!this.list[key]) {
                this.list[key] = []
            }
            this.list[key].push(fn)
        }
        // 发布消息
        shopObj.trigger = function () {

            // 取key
            var key = Array.prototype.shift.call(arguments)
            var fns = this.list[key]
            // 判断
            if (!fns || fns.length == 0) {
                return
            }
            for (var i = 0, fn; fn = fns[i++];) {
                // fn.apply(this, arguments)
                fn(...arguments)
            }
        }
        shopObj.listen('red', function (size) {
            console.log(`尺码是${size}`);
        })
        shopObj.listen('black', function (size) {
            console.log(`尺码是${size}`);
        })
        // 小红
        shopObj.trigger('red', 42)
        // 明
        shopObj.trigger('black', 43)
        // 一次封装
        var event = {
            list: [],
            listen: function (key, fn) {
                if (!this.list[key]) {
                    this.list[key] = []
                }
                this.list[key].push(fn)
            },
            trigger: function () {

                // 取key
                var key = Array.prototype.shift.call(arguments)
                var fns = this.list[key]
                // 判断
                if (!fns || fns.length == 0) {
                    return
                }
                for (var i = 0, fn; fn = fns[i++];) {
                    // fn.apply(this, arguments)
                    fn(...arguments)
                }
            }
        }
        // 取消订阅
        event.remove = function (key, fn) {
            var fns = this.list[key]
            if (!fns) {
                return false
            }
            if (!fn) {
                fn && (fns.length = 0)
            } else {
                for (var i = fns.length - 1; i >= 0; i--) {
                    var _fn = fns[i]
                    if (_fn == fn) {
                        fns.splice(i, 1)
                    }
                }
            }
        }

        // 二次封装
        var Event = (function () {
            var list = {},
                listen,
                trigger,
                remove;
            listen = function (key, fn) {
                    if (!this.list[key]) {
                        this.list[key] = []
                    }
                    this.list[key].push(fn)
                },
                trigger = function () {

                    // 取key
                    var key = Array.prototype.shift.call(arguments)
                    var fns = this.list[key]
                    // 判断
                    if (!fns || fns.length == 0) {
                        return
                    }
                    for (var i = 0, fn; fn = fns[i++];) {
                        // fn.apply(this, arguments)
                        fn(...arguments)
                    }
                },
                remove = function (key, fn) {
                    var fns = this.list[key]
                    if (!fns) {
                        return false
                    }
                    if (!fn) {
                        fn && (fns.length = 0)
                    } else {
                        for (var i = fns.length - 1; i >= 0; i--) {
                            var _fn = fns[i]
                            if (_fn == fn) {
                                fns.splice(i, 1)
                            }
                        }
                    }
                }
            return {

                listen,
                trigger,
                remove
            }
        })()

改进异步操作中的强耦合

业务场景

假如正在开发个商城网站, 网站里有header头部、 nav导航、消息列表、购物车等模块。这几个模块的渲染有一个共同的前提条件,就是必须先用aj ax异步请求获取用户的登录信息。这是很正常的,比如用户的名字和头像要显示在header模块里,而这两个字段都来自用户登录后返回的信息

login. succ( function(data){//登入。功能(数据){
header. setAvatar( data.avatar); //设置header模块的头像
nav. setAvatar( data.avatar ); //设置导航模块的头像
message. refresh(); //刷新消息列表
cart. refresh(); //刷新购物车列表
});

强耦合

现在必须了解header模块里设置头像的方法叫setAvatar、购物车模块里刷新的方法叫refresh,这种耦合性会使程序变得僵硬,heade r模块不能随意再改变setAvata r的方法名,它自身的名字也不能被改为header1 ,header2 等到有一天,项目中又新增了一个收货地址管理的模块,在最后部分加上这行代码:

login. succ( funct ion(data){
header . setAvatar( data . avatar); //设置header 模块的头像
nav. setAvatar( data.avatar ); //设置导航模块的头像
message. refresh(); //刷新消息列表
cart. refresh(); //刷新购物车列表
address. refresh();
});

发布订阅模式实现低耦合

用发布订阅模式重写之后,对用户信息感兴趣的业务模块将自行订阅登录成功的消息事件。当登录成功时,登录模块只需要发布登录成功的消息,而业务方接受到消息之后,就会开始进行各自的业务处理,登录模块并不关心业务方究竟要做什么,也不想去了解它们的内部细节

// 你只管发布
$.ajax('http://xx. com? Login', function (data) { //登录成功 
    login.trigger('loginSucc ', data); // 发布登录成功的消息   
});
// 我只管监听---各模块监听登录成功的消息
var header = (function () { // header 模块       
    login.listen(' loginSucc', function (data) {
        header.setAvatar(data.avatar);
    });
    return {
        setAvatar: function (data) {
            console.log('设置header 模块的头像');
        }
    }
})()
var nav = (function () { // nav 模块
    login.listen(' LoginSucc', function (data) {
        nav.setAvatar(data.avatar);
    });
    return {
        setAvatar: function (avatar) {
            Iconsole.log('设置nav 模块的头像');
        }
    }
})();