一直想学习,so,最近学习了一下,做个笔记加深理解
在JavaScript设计模式这本书中,是将卫星通信来解释这个原理的,当然,照此,我们可以举个例子:
张三去公司A面试,李四也去公司A面试。。。,(这就相当于张三,李四都订阅了公司A),公司A过一段时间就发布通知啦,让张三,李四去公司A面试(公司A发布消息,订阅者收到消息),结果,张三有事不去面试了,这就导致张三不再接受A的消息(张三取消订阅公司A)。
先来个简单栗子:
- 要指定一个发布者(公司A);
- 给发布者添加一个列表,用于存放回调函数以便通知订阅者;(张三,李四中的函数)
- 最后发布通知的时候,发布者会遍历这个列表,依次触发里面存放的订阅者回调函数;(暂不考虑取消订阅)
let Observer = {}; // 公司A
Observer.clietList = []; // 招聘者列表
Observer.subscibe = (fn) => { // 收集招聘者
Observer.clietList.push(fn)
}
Observer.trigger = (...args) => { //发布消息,依次通知招聘者
Observer.clietList.forEach((fn) => {
fn.apply(this, args)
})
}
Observer.subscibe((name) => { // 某人投了A简历
console.log(`${name}去公司A招聘`)
})
Observer.trigger('张三') // 发布消息
// 输出 张三去公司A招聘
以上就是一个很简单的观察者模式,但是如《JavaScript设计模式》书中栗子而言,我们需要一个消息类型,也就是招聘者门都去A面试,那A有很多岗位,下面以前端为例,类似vue,react这种。而且,为了防止列表的污染,我们和书中一样,将以闭包的方式进行改写
let Observer = (() => {
// 防止消息队列暴露而被篡改,将消息容器作为静态私有变量保存
const clietList = [] // 发布者列表
const subscibe = (key, fn) => { // 增加订阅者函数
if (!clietList[key]) {
clietList[key] = []
}
clietList[key].push(fn)
}
const trigger = (...args) => { // 发布消息函数
let key = args.shift(),
fns = clietList[key]
fns.forEach((item, index) => {
fns[index].apply(this, args)
})
}
const remove = function(key, fn) {
let fns = clietList[key]
if (!fns) return;
if (!fn) {
clietList[key] = [] //不传fn,则直接清空对应的key订阅集合
} else {
fns.forEach((item, index) => {
if (item === fn) {
fns.splice(index, 1) // 删除订阅者的回调函数
}
})
}
}
return {
subscibe,
trigger,
remove
}
})();
调用:
Observer.subscibe('Vue', fn1 = (time) => {
console.log(`张三面试时间${time}`)
})
Observer.subscibe('React', fn2 = (time) => {
console.log(`李四面试时间${time}`)
})
Observer.subscibe('Vue', fn3 = (time) => {
console.log(`王五面试时间${time}`)
})
Observer.trigger('Vue', '2018-10-16')
Observer.trigger('React', '2018-10-17')
Observer.remove('Vue', fn1)
Observer.trigger('Vue', '2018-10-16')
Observer.trigger('React', '2018-10-17')
输出:
设计模式书中,还举了两个实际栗子来分别阐述,一般解耦与对象间解耦
我这边就照着文章的三位工程师的栗子写了一下,代码有所不同,但是原理都一样:
开发一个评论功能,工程师A,B,C三位各自开发自己的模块,为防止污染,都以闭包形式。怎么将其联系起来,实现功能。
<div>
<div id='msg_num'></div>
<div id='msg'></div>
<div>
<input type="text" name="" id='user_input'>
<button id='user_submit'>提交</button>
</div>
</div>
function $(id) {
return document.getElementById(id)
};
(function () {
function addMsgItem(e) {
console.log(e)
let text = e.text,
ul = $('msg'),
li = document.createElement('li'),
span = document.createElement('span');
span.innerHTML = '删除';
li.innerHTML = text;
span.onclick = function () {
ul.removeChild(li);
Observer.trigger('removeCommentMessage', {
num: -1
})
}
li.appendChild(span);
ul.appendChild(li)
}
Observer.subscibe('addCommentMessage', addMsgItem);
})();
(function () {
function changeMsgNum(e) {
let num = e.num;
$('msg_num').innerHTML = parseInt($('msg_num').innerHTML ? $('msg_num').innerHTML : 0) + num;
}
Observer.subscibe('addCommentMessage', changeMsgNum);
Observer.subscibe('removeCommentMessage', changeMsgNum);
})();
(function() {
$('user_submit').onclick = function() {
let text = $('user_input');
if (!text.value) {
alert ('请输入');
return;
};
Observer.trigger('addCommentMessage', {
text: text.value,
num: 1
})
text.value = '';
}
})();
效果:
推荐各位可以去看一看这本书,当然,我也是菜鸟,啃了之后似懂非懂。。。哈哈