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设计模式与开发实践》