定义:观察者模式,又叫发布订阅者模式,又叫消息系统,又叫消息机制,又叫自定义事件,解决主体与观察者之间的耦合问题
特点:
1 解决的是耦合问题(类与类之间,对象之间,类与对象之间,模块之间)
2 对于任何一个观察者来说,其他观察者的改变不会影响自身
3 对于任何一个对象来说,既可以是观察者,也可以是被观察者
jQuery中的观察者模式
$.CallBacks()方法执行的结果得到一个观察者对象:
观察者对象有一个方法叫add,用来订阅消息的
观察对象有一个方法叫fire,用来发布消息
// 定义一个观察者对象
var Observer = $.Callbacks();
// 订阅一个消息,订阅today消息,当接收该消息的时候,我们发布今题的日期
Observer.add('today', function () {
console.log(arguments)
})
document.onclick = function () {
Observer.fire('today', 'hello', 'word', 'success')
}
得到结果如下:
js中观察者模式的实现
观者者对象必须具备两个方法
regist 用来注册消息
第一个参数表示消息的名称
第二个参数表示回调函数
fire 用来触发消息
第一个参数表示消息的名称
第二个参数表示传递数据
实现方法如下:
// 定义观察者对象
var Observer = (function () {
// 通过闭包,存储的回调函数对用户来说是不可见的
var __message = {};
// 我们希望resit和fire是可以访问的
return {
/**
* 用来注册消息
* @type 消息的名称
* @fn 回调函数
**/
regist: function (type, fn) {
// 将回调函数注册到__message变量中
// 对于message消息管道来说,我们要根据type开辟不同的存储空间,来存储fn(放在数组中)
// 判断有没有type类型的存储空间
if (__message[type]) {
// 存储回调函数
__message[type].push(fn);
} else {
// 开辟新的存储空间存储回调函数
__message[type] = [fn];
}
},
/**
* 用来触发消息
* @type 消息的名称
* @data 传递的数据
**/
fire: function (type, obj) {
// 适配参数
var params = {
// 这里做了优化,可以传入作用域和参数,作用域要传入一个对象,参数要传入一个数组
context: (obj && obj.context) || null,
args: (obj && obj.args) || []
}
//把type传入函数参数中,放在最前边,作为第一个参数
params.args.unshift(type)
// 在消息管道中,寻找有没有该类型的消息,所以遍历消息管道
if (__message[type]) {
// __message[i] 是一个数组,遍历这个数组并执行
for (var i = 0; i < __message[type].length; i++) {
// 我们要将自定义数据传递进来
// __message[type][i](data)
// 我们想实现jqyery的Callbacks触发方式
// call 将参数意义列举进来,apply将参数作为数组传递
__message[type][i].apply(params.context, params.args)
}
}
},
// 有时候我们也希望注销事件
/**
* 注销事件
* @type 消息类型
* @fn 消息回调函数
**/
remove: function (type, fn) {
// 将fn从type的消息队列中删除
if (__message[type]) {
// 判断该队列中是否拥有fn
// 从数组中删除一个成员用什么方法?
// 从前遍历还是从后遍历?
// 我们从后向前遍历,因为删除的某一项只会影响该项后面的成员,前面的成员索引值不会影响,所以可以正常遍历
for (var i = __message[type].length - 1; i >= 0; i--) {
// 判断该项是否是fn
if (__message[type][i] === fn) {
// 删除这一项
__message[type].splice(i, 1);
}
}
}
}
}
})()
使用封装的观察者方法如下:
var hh = function () {
console.log(arguments, this)
}
// 订阅消息
Observer.regist('say', hh)
//定义作用域
var obj = {
title: 'mm'
}
//触发消息
Observer.fire('say', {
// 传递一个作用域,希望我们的回调函数在obj作用域下执行
context: obj,
args: ['a','b']
})
/*
* 如果要注销哪个消息里的函数,就把哪个消息和它对应的函数传入进去,进行注销
* //注销这个say函数
* Observer.remove('say', hh)
*/
得到结果如下: