###1 订阅/发布模式说明
什么是订阅/发布模式呢?
举个简单的例子。我们在开发网站常常有这样的一种情况。就是用户登录过的主界面和没登录过
的主界面显示是不一样的,而没有使用订阅/发布模式的话。我们很可能会写出下面的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//登录成功情况下
login.succ(function( data ){
header.setAvatar( data.avatar);
nav.setAvatar( data.avatar );
message.refresh();
cart.refresh();
address.refresh();
});
//登录失败情况下
login.fail(function( data ){
header.setAvatar( data.avatar);
nav.setAvatar( data.avatar );
message.refresh();
cart.refresh();
address.refresh();
});
/*这两种情况都有相应的处理逻辑,假如现在又出现了新的模块的话。我们又要在上述的两种情况下
加入逻辑代码,十分不利于维护。*/
###2 订阅/发布模式的实践
如果我们利用订阅/发布模式的话,整个流程就十分清晰了。
模式解释:发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象
的状态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模
型来替代传统的发布—订阅模式。
流程如下:
- 创建发布者对象,它具有订阅者的对象数组(我们网页中的各个模块)。以及相应的发布通知这些对象执行相应方法。
- 创建订阅者。它具有接收到发布信息的执行方法(模块初始化方法)。
- 发布者接收到通知(登录成功)。
- 对所有订阅者对象数组进行通知。
1 | //实践代码 |
###2.1改进
以上就是一个简单的订阅/发布的实现。但上述代码还有问题,比如我们给每一个发布者都赋予了订阅者的参数列表是一种
资源浪费。我们的订阅者至少需要知道发布者的名字才能接受到信息。硬编码十分明显。接下来。我们可以使用一个全局的订阅、
发布对象,抽象出一个“中介者”的对象。他负责把订阅者以及发布者隔离开。
以一个售楼处以及客户的关系作为例子。同样在程序中,发布—订阅模式可以用一个全局的 Event 对象来实现,订阅者不需要
了解消息来自哪个发布者,发布者也不知道消息会推送给哪些订阅者,Event 作为一个类似“中介者”的角色,把订阅者和发布
者联系起来。
示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57var Event = (function () {
//创建订阅者对象数组
var clientList = {},
listen,
trigger,
remove;
//订阅者订阅实现
listen = function (key, fn) {
if (!clientList[key]) {
clientList[key] = [];
}
clientList[key].push(fn);
};
//发布者发布消息实现
trigger = function () {
var key = Array.prototype.shift.call(arguments),
fns = clientList[key];
if (!fns || fns.length === 0) {
return false;
}
for (var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
};
//订阅者取消订阅
remove = function (key, fn) {
var fns = clientList[key];
if (!fns) {
return false;
}
if (!fn) {
fns && (fns.length = 0);
} else {
for (var l = fns.length - 1; l >= 0; l--) {
var _fn = fns[l];
if (_fn === fn) {
fns.splice(l, 1);
}
}
}
};
//暴露的公共接口
return {
listen: listen,
trigger: trigger,
remove: remove
}
})();
//小红订阅消息
Event.listen('squareMeter88', function (price) {
// 小红订阅消息,小红想要订阅88平方米的楼房价格。
//我们发现并不需要小红这个对象传入,只需要传入要监听的对象,以及回调函数即可
console.log('价格= ' + price); // 这里为小红接收到信息所做出的业务操作。输出:'价格=2000000'
});
//发布者发布消息
Event.trigger('squareMeter88', 2000000); // 售楼处发布88平方米的楼房价格消息。
到这里为止。一个比较完善的模式流程就差不多搞定了。
###2.2 界面实践
接下来我们用一个关于界面的示例来形象说明如何使用它。
需求:比如现在有两个模块,a 模块里面有一个按钮,每次点击按钮之后,b 模块里的 div 中会显示
按钮的总点击次数,我们用全局发布—订阅模式完成下面的代码,使得 a 模块和 b 模块可以在保
持封装性的前提下进行通信。
1 | <html> |
本文小结:
本章我们学习了发布—订阅模式,也就是常说的观察者模式。发布—订阅模式在实际开发中非
常有用。
- 它的优点:一为时间上的解耦,二为对象之间的解耦。它的应用非常广泛,既可以用在异步编程
中,也可以帮助我们完成更松耦合的代码编写。发布—订阅模式还可以用来帮助实现一些别的设计
模式,比如中介者模式。从架构上来看,无论是 MVC 还是 MVVM,都少不了发布—订阅模式的参与
,而且 JavaScript 本身也是一门基于事件驱动的语言。- 它的缺点:创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息
最后都未发生,但这个订阅者会始终存在于内存中。另外,发布—订阅模式虽然可以弱化对象之间的
联系,但如果过度使用的话,对象和对象之间的必要联系也将被深埋在背后,会导致程序难以跟踪
维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个 bug 不是件轻松的事情。
#3 小发现
在编码的时候发现了原生JavaScript的onclick和click的区别,现在放入这里供大家参考
区别:
- 原生javascript的click在w3c里边的阐述是DOM button对象,也是html DOM click() 方法,
可模拟在按钮上的一次鼠标单击。button 对象代表 HTML 文档中的一个按钮。button元素没有默
认的行为,但是必须有一个 onclick 事件句柄以便使用。- onclick是一个事件,Event对象 。支持该事件的 JavaScript 对象:button, document,
checkbox, link, radio, reset, submitHTML DOM Event 对象,代表事件的状态,比如事
件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。事件通常与函数结合使用,函数
不会在事件发生前被执行!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 <html>
<head>
<script type="text/javascript">
function clickButton()
{
document.getElementById('button1').click()
}
function alertMsg()
{
alert("Button 1 was clicked!")
}
</script>
</head>
<body onload="clickButton()">
<form>
<input type="button" id="button1" onclick="alertMsg()" value="Button 1" />
</form>
</body>
</html>