js如何实现观察者模式

定义:观察者模式,又叫发布订阅者模式,又叫消息系统,又叫消息机制,又叫自定义事件,解决主体与观察者之间的耦合问题

特点:

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)
*/

 得到结果如下:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值