《JavaScript设想形式与开辟实践》读书笔记。
宣布-定阅形式又叫观察者形式,它定义了对象之间的一种一对多的依靠关联。当一个对象的状况发作转变时,一切依靠它的对象都将获得关照。
比方:在segmentfault我们关注了某一个题目,这个时刻能够说是定阅了这个题目的音讯。当该题目有了新的回复、批评的时刻,segmentfault体系就会遍历关注了这个题目的用户,一次给用户发音讯。
如今看看怎样一步步完成宣布-定阅形式。
起首,指定好宣布者(如 segmentfault 的 题目体系)
接着,给宣布者增添一个缓存列表,用于存放回调函数以便关照定阅者(题目体系的纪录表)
末了,当宣布者宣布音讯的时刻,会遍历缓存列表,顺次触发内里的回调函数(遍历纪录表,逐一发音讯)
我们还能够在回调函数内里到场一些参数,定阅者能够吸收这些参数,举行各自的处置惩罚。
var sgQuestionSystem = {}; // 定义segmentfault的题目体系
/*
* 缓存列表
* clientList: {
* key: [
* id: <int>, // 唯一标识
* fn: null // 存放回调函数
* ]
* }
*
*/
sgQuestionSystem.clientList = {};
/*
* 增添定阅者(定阅函数),将定阅的范例与回调函数到场缓存列表
* key: 音讯的范例
* id: 定阅的唯一标识
* fn: 定阅的回调函数
*/
sgQuestionSystem.listen = function(key, id, fn) {
if(!this.clientList[key]) { // 若缓存列表没有该范例的音讯,给该类音讯初始化
this.clientList[key] = []
}
this.clientList[key].push({ // 将定阅的id, 回调函数增添到对应的音讯列内外
id: id,
fn: fn
})
}
// 宣布音讯(宣布函数), 顺次关照定阅者
sgQuestionSystem.trigger = function () {
var key = Array.prototype.shift.call(arguments), // 掏出音讯范例
fns = this.clientList[key]; // 掏出该音讯对应的回调函数鸠合
if(!fns || fns.length == 0) { // 若定阅列表没有该范例的回到函数,则返回
return false;
}
for(var i = 0; i< fns.length; i++) {
fns[i].fn.apply(this, arguments); // arguments是宣布音讯时附送的参数,去掉了key
}
}
如今,我们来举行一些简朴的测试:
// 张三定阅题目A
sgQuestionSystem.listen('questionA', 3, function(questionTitle, content) {
console.log('张三您在早前定阅了题目:questionA');
console.log('现' + questionTitle + '有了新动态');
console.log('内容为:' + content);
});
// 李四定阅题目A
sgQuestionSystem.listen('questionB', 4, function(questionTitle, content) {
console.log('李四您在早前定阅了题目:questionB');
console.log('现' + questionTitle + '有了新动态');
console.log('内容为:' + content);
})
// 题目体系宣布音讯
sgQuestionSystem.trigger('questionA', '题目A', '王五回复了题目A');
sgQuestionSystem.trigger('questionB', '题目B', '吴六回复了题目B');
至此,我们完成了一个简朴的宣布-定阅形式,定阅者能够定阅本身感兴趣的事宜了。
列位看官,看累了吗?
看累的话点一下珍藏,以便您看续集。若您照样精神抖擞,那就继承撸吧。
上部份,我们完成了一个题目体系的宣布-定阅形式,如今,我们要完成一个文章的宣布-定阅形式,这时刻,该怎么办?将上面的代码ctrl + c, ctrl + v, 再改下名字?照样有更好的解决方案?
答案显然是有的,我们能够将宣布-定阅功用模块提掏出来,放在一个零丁的对象内里:
var publishSubscribeEvent = {
/*
* 缓存列表
* clientList: {
* key: [
* id: <int>, // 唯一标识
* fn: null // 存放回调函数
* ]
* }
*
*/
clientList: {},
/*
* 增添定阅者(定阅函数),将定阅的范例与回调函数到场缓存列表
* key: 音讯的范例
* id: 定阅的唯一标识
* fn: 定阅的回调函数
*/
listen: function(key, id, fn) {
if(!this.clientList[key]) {
this.clientList[key] = []
}
this.clientList[key].push({
id: id,
fn: fn
})
},
// 宣布音讯(宣布函数), 顺次关照定阅者
trigger: function () {
var key = Array.prototype.shift.call(arguments),
fns = this.clientList[key];
if(!fns || fns.length == 0) {
return false;
}
for(var i = 0; i< fns.length; i++) {
fns[i].fn.apply(this, arguments);
}
}
}
再定义一个装置宣布-定阅的函数installPublishSubscribeEvent,这个函数能够给一切对象都动态装置宣布-定阅功用:
var installPublishSubscribeEvent = function(obj) {
for(var i in publishSubscribeEvent) {
obj[i] = publishSubscribeEvent[i];
}
}
再来测试一番,我们给文章对象 sgArticleSystem 动态增添宣布-定阅功用:
var sgArticleSystem = {};
installPublishSubscribeEvent(sgArticleSystem );
// 张三定阅文章A动态
sgArticleSystem.listen('articleA', 3, function(articleTitle, content) {
console.log('张三您在早前定阅了文章:articleA');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
});
// 李四定阅文章B动态
sgArticleSystem.listen('articleB', 4, function(articleTitle, content) {
console.log('李四您在早前定阅了文章:articleB');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
});
// 文章体系宣布音讯
sgArticleSystem.trigger('articleA', 'JavaScript设想形式之宣布-定阅形式', '作者修改了文章');
sgArticleSystem.trigger('articleB', 'JavaScript设想形式之战略形式', '王五用户批评了该文章');
好了,该代码经由自测是没有什么题目的,假如列位看官发现题目,迎接反应。如今,我们已能够给我们指定的对象装置宣布-定阅形式,然则,是否是还少了点什么功用呢?
答案就是少了作废定阅事宜的功用。比方张三倏忽不想关注该题目的更新动态了,为了防止继承收到题目体系推送过来的音讯,张三须要作废之前定阅的事宜。如今,我们给 publishSubscribeEvent 对象增添 remove 要领。
publishSubscribeEvent.remove = function(key, id) {
var fns = this.clientList[key];
if(!fns) { // 假如key对应的音讯没人定阅,直接返回
return false;
}
if(!id) { // 假如没传详细的唯一标识,则作废key的一切对应音讯
fns && (fns.length = 0);
} else {
for(var l = fns.length - 1; l >=0; l--) {
var _id = fns[l].id;
if(_id == id) {
fns.splice(l, 1); // 删除定阅者的回调函数
}
}
}
}
// 测试代码
var sgArticleSystem = {};
installPublishSubscribeEvent(sgArticleSystem );
// 张三的定阅
sgArticleSystem.listen('articleA', 3, function(articleTitle, content) {
console.log('张三您在早前定阅了文章:articleA');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
});
// 李四的定阅
sgArticleSystem.listen('articleA', 4, function(articleTitle, content) {
console.log('李四您在早前定阅了文章:articleA');
console.log('现' + articleTitle+ '有了新动态');
console.log('内容为:' + content);
});
sgArticleSystem.remove('articleA', 3); // 删除张三的定阅
sgArticleSystem.trigger('articleA', 'JavaScript设想形式之宣布-定阅形式', '作者修改了文章');
上面的代码跟原著有所不同,原著是在删除定阅的时刻是用对照回调函数的,而我是往缓存列表加了一个唯一的标识,用于辨认。
至此,我们的宣布-定阅形式第一部份已结束,迎接人人珍藏批评。
附:
JavaScript设想形式之宣布-定阅形式(观察者形式)-Part2
JavaScript数据结构和算法系列:
JS 栈
JS 行列-优先行列、轮回行列
JavaScript设想形式系列:
JavaScript设想形式之战略形式