在我的理解中,观察者模式,又叫发布/订阅模式,是一种是一种很便捷的用于不同模块之间相互通信的一种方式,类似于事件又不同于事件。

在我目前的理解中,观察者模式应该有三个部分组成,发布者,订阅者,管理平台,发布者发布信息到管理平台,管理平台收集到信息之后分发给订阅者,用更浅白的语言来形容我对观察者模式的理解就是函数调用,在不同模块之间通过函数调用进行通信。

言语之间不太好解释,还是来看看代码,可以更快的理解我所说的意思,先看管理平台是如何工作的:

let event = [];
class Test{
	static addEvent(type,fn){
		if(!event[type]){
			event[type] = [fn];
		}else{
			event[type].push(fn);
		}
	}
	static dispath(type){
		let s = [] ,i=1;
		while (arguments[i])
		{
			s.push(arguments[i]);
			i++;
		}
		if(!event[type]){
			console.error('no Event!');
		}else{
			for(let i = 0;i 
  

我这里起名字的时候是用了Event的那一套名字,但和Event那一套其实没有什么关系,完全可以换成不同的名字。

看上面的代码:

let event = [];

这是一个数组,用来存放订阅者的信息,也就是订阅的事件类型,和触发函数,最终所形成的样式是这样的:

[
    'type1' : [fn1,fn2,fn3],
    'type2' : [fn4],
    'type3' : [fn5,fn6]
]

实际上就是有三个订阅者订阅type1,触发事件分别为fn1,fn2,fn3,后面等同。当发布者发布type1的时候,就会调用函数fn1,fn2,等等。

因为订阅者须要提前订阅才能接收到被订阅的内容,所以,我们先看订阅者是如何订阅的:

static addEvent(type,fn){
		if(!event[type]){
			event[type] = [fn];
		}else{
			event[type].push(fn);
		}
	}

在node.js中我们声明了一个类名为Test,里面有个静态方法 addEvent,两个参数,第一个是订阅的事件类型,第二个是触发函数,当还没有人订阅该事件的时候,就先创建该事件的函数空间,实际上就是一个数组,把事件名当索引,把触发事件存到该索引的元素下,如果已经有人订阅了该事件,就直接把触发事件push到对应索引的元素下。这样就可以记录下所有订阅者订阅的事件了。


然后是发布者发布事件:

static dispath(type){
		let s = [] ,i=1;
		while (arguments[i])
		{
			s.push(arguments[i]);
			i++;
		}
		if(!event[type]){
			console.error('no Event!');
		}else{
			for(let i = 0;i 
  

这也是一个静态函数,是用来把发布者发布的消息分发到每一个订阅者,while循环中是对参数的处理,把除第一个参数之外的所有参数集中到一个数组里,方面后面展开,这样可以还原到发布消息时的参数顺序,方面订阅者更好的接收参数,然后就是在所有的订阅者中找到第一个参数(也就是发布者发布的事件)所对应的所有触发函数,然后循环调用一次并把除事件之外的参数传过去。


这样的话,管理平台就组建好了。下面就是订阅,因为不先订阅是不可能收到发布者发布的消息的。

Test.addEvent('A',function (name,age){
	console.log('hello,this is "A" Event 1,has arg:','name:',name,'age:',age);
});
Test.addEvent('B',function (num){
	console.log('hello,this is "B" Event 2,has arg:',num);
});
Test.addEvent('C',function (obj){
	console.log('hello,this is "C" Event 3,has arg:',obj);
});
Test.addEvent('D',function (arr){
	console.log('hello,this is "D" Event 4,has arg:',arr);
});
Test.addEvent('E',function (date){
	console.log('hello,this is "E" Event 5,has arg:',date);
});

上面我们订阅了五个事件,每个事件所要接收的参数类型都不一样。并且订阅类型A须要接收两个参数,

订阅是不是很简单?好吧,其实发布者更简单:

Test.dispath('A','xiaoming',17);
Test.dispath('B',123);
Test.dispath('C',{say:'hello word'});
Test.dispath('D',[1,2,3]);
Test.dispath('E',new Date());

发布事件ABCDE,后面跟上发布的内容,然后订阅者就可以接发布时的顺序来接收参数。在项目中的不同模块之间进行通信就变得非常简单了。

到了这里,我想大家应该也都发现了我所说的订阅/发布 实际上就是函数调用是怎么回事了,订阅者把将要因事件而被触发的函数通过函数调用传到管理平台上,发布者把须要发布的事件也通过函数调用传到管理平台上,然后管理平台则根据不同的事件调用不同的函数。

代码不够精练,格式不够标准......

好吧,完了。

------------------------------------------------------------------------------------

另外,记录一点和该话题没多大关系但用到的一点内容,就是参数传递那一块,因为管理平台不能确定发布者会传多少参数进来,所以用到了Node.js的一个特性:arguments,在node.js中不论函数有有没有声明接收的参数,每个函数中都会有一个局部变量arguments,它是一个object对象,会把外界调用该函数时所传进来的参数整合进来,然后把传参的顺序做为属性,比如上面的A事件,在管理平台的dispath函数中arguments就是这个样子的:

{
    '0' : 'A',
    ‘1’:‘xiaoming’,
    '2' : 17
}

对象直接用  ...  这个符号拆分的话会报错,所以我把arguments用while重新组合成了数组,然后用 ...符号来拆分数组再进行参数传递。