JavaScript设想形式之宣布-定阅形式(观察者形式)-Part1

《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设想形式之战略形式

    原文作者:wuweisen
    原文地址: https://segmentfault.com/a/1190000007409168
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞