5、发布—订阅模式

1、特点

发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在JavaScript 开发中,我们一般用事件模型来替代传统的发布—订阅模式。

2、demo

订阅房子开售的消息


// 首先要指定好谁充当发布者(比如售楼处);
// 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(售楼处的花名册);
// 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数(遍历花名册,挨个发短信)。
// 另外,我们还可以往回调函数里填入一些参数,订阅者可以接收这些参数。这是很有必要的, 比如售楼处可以在发给订阅者的短信里加上房子的单价、面积、容积率等信息,订阅者接收到这 些信息之后可以进行各自的处理
// //自定义事件
var salesOffices = {}; //定义售楼处

salesOffices.clientList = []; //缓存列表,存放订阅者的回调函数
salesOffices.listen = function(fn) { //添加订阅者
    this.clientList.push(fn); //订阅的信息添加进缓存列表
};

salesOffices.trigger = function() { //发布信息
    for (var i = 0, fn; fn = this.clientList[i++];) {
        fn.apply(this, arguments);
    }
}

salesOffices.listen(function(price, squareMeter) {
    console.log('价格' + price);
    console.log('squareMeter= ' + squareMeter);
});

salesOffices.listen(function(price, squareMeter) {
    console.log('价格=' + price);
    console.log('squareMeter= ' + squareMeter);
});

salesOffices.trigger(2000000, 88); // 输出:200 万,88 平方米
salesOffices.trigger(3000000, 110); // 输出:300 万,110 平方米

// 优化

var salesOffices = {}; // 定义售楼处

salesOffices.clientList = {}; // 缓存列表,存放订阅者的回调函数

salesOffices.listen = function(key, fn) {
    if (!this.clientList[key]) { // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
        this.clientList[key] = [];
    }

    this.clientList[key].push(fn); // 订阅的消息添加进消息缓存列表
};

salesOffices.trigger = function() { // 发布消息
    var key = Array.prototype.shift.call(arguments), // 取出消息类型
        fns = this.clientList[key]; // 取出该消息对应的回调函数集合
    if (!fns || fns.length === 0) { // 如果没有订阅该消息,则返回
        return false;
    }
    for (var i = 0, fn; fn = fns[i++];) {
        fn.apply(this, arguments); // (2) // arguments 是发布消息时附送的参数
    }
};

salesOffices.listen('squareMeter88', function(price) { // 小明订阅88 平方米房子的消息
    console.log('价格= ' + price); // 输出: 2000000
});

salesOffices.listen('squareMeter110', function(price) { // 小红订阅110 平方米房子的消息
    console.log('价格= ' + price); // 输出: 3000000
});

salesOffices.trigger('squareMeter88', 2000000); // 发布88 平方米房子的价格
salesOffices.trigger('squareMeter110', 3000000); // 发布110 平方米房子的价格

全局的发布

// 全局的发布—订阅对象里只有一个clinetList 来存放消息名和回调函数,大家都通过它来订
// 阅和发布各种消息,久而久之,难免会出现事件名冲突的情况,所以我们还可以给Event 对象提
// 供创建命名空间的功能。

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);
                }

                cache[key].push(fn);
            };

        _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) {
                        _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;

        };
    }
})

Event.listen('squareMeter88', function(price) { // 小红订阅消息
    console.log('价格= ' + price); // 输出:'价格=2000000'
});
Event.trigger('squareMeter88', 2000000); // 售楼处发布消息

参考文献:参考文献:《JavaScript设计模式与开发实践》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值