美文网首页让前端飞前端教程一大堆
javascript设计模式之观察者模式

javascript设计模式之观察者模式

作者: 崔小叨 | 来源:发表于2019-02-10 01:38 被阅读0次

    要理解观察者模式,可以类比vue中的EventBus,其实就是一个全局的观察者对象($bus),上面有注册事件($bus.on())和发送事件($bus.emit())的方法,当然因为需要会注册很多事件,所以内部还有一个事件列表属性_events来存储注册的事件。下面为学习笔记,对观察者模式做简单实现。

    基于上面的思路,首先要有一个对象,它有一个私有的列表属性和对外暴露的两个方法

    let Observer = (()=>{
        _events:[];
        return {
            retister:()=>{},
            issue:()=>{},
        }
    })()
    

    接下来一步步实现。

    首先,观察者对象内部要有一个存储事件的列表属性

    let Observer = (()=>{
        // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存
        let _events = [];
    })()
    

    其次是注册事件的register方法,需要两个参数:需要观察的事件名称type和这个事件被触发后具体的执行内容fn

    let Observer = (()=>{
        // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存
        let _events = {};
        return {
            register: (type,fn)=>{
                //如果此消息类型不存在则应该创建一个(判断对象上是否有某个属性)
                // if (typeof(_events[type])==='undefined'){
                if (!(type in _events)){
                    _events[type] = [fn];
                } else {//如果此消息类型已存在,则直接将对应动作推入消息队列
                    _events[type].push(fn);
                }
            },
        }
    })()
    

    再然后就是触发事件的issue方法,也需要两个参数:要触发的事件type及传递过去的参数arg

    let Observer = (()=>{
        // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存
        let _events = {};
        return {
            register: (type,fn)=>{
                //如果此消息类型不存在则应该创建一个(判断对象上是否有某个属性)
                // if (typeof(_events[type])==='undefined'){
                if (!(type in _events)){
                    _events[type] = [fn];
                } else {//如果此消息类型已存在,则直接将对应动作推入消息队列
                    _events[type].push(fn);
                }
            },
            issue: (type,arg)=>{
                // 如果没有此类型,返回
                // if (typeof (_events[type]) === 'undefined'){
                if (!(type in _events)) {
                    return;
                }
                for (let i=0;i<_events[type].length;i++){
                    _events[type][i](arg);
                }
            },
        }
    })()
    

    最后,可以再加一个可以移除已监听事件的remove方法

    let Observer = (()=>{
        // 防止事件队列暴露而被篡改故将事件容器作为静态私有变量保存
        let _events = {};
        return {
            register: (type,fn)=>{
                //如果此消息类型不存在则应该创建一个(判断对象上是否有某个属性)
                // if (typeof(_events[type])==='undefined'){
                if (!(type in _events)){
                    _events[type] = [fn];
                } else {//如果此消息类型已存在,则直接将对应动作推入消息队列
                    _events[type].push(fn);
                }
            },
            issue: (type,arg)=>{
                // 如果没有此类型,返回
                // if (typeof (_events[type]) === 'undefined'){
                    if (!(type in _events)) {
                    return;
                }
                for (let i=0;i<_events[type].length;i++){
                    _events[type][i](arg);
                }
            },
            // 此方法可以类比document.removerEventListener(evt,fn)
            remove: (type,fn)=>{
                // 如果这个类型的事件存在
                if (_events[type] instanceof Array){
                    for (let i = 0; i < _events[type].length; i++) {
                        _events[type][i] === fn && _events[type].splice(i,1);
                    }
                }
            },
            // 因为外部获取不到私有属性`_events`,所以临时用这个方法检查`remove`方法是否生效
            check (){
                console.log('_events事件队列',_events);
            }
        }
    })()
    

    至此一个基本的观察者对象就写出来了,接下来跑一下看效果。

    // 注册的事件被触发后需要执行的动作
    function handler (val){
        console.log('val:',val*2);
    }
    
    // 注册事件及对应的执行动作
    Observer.register('eventName',handler);
    
    // 触发事件
    function test (){
        Observer.issue('eventName',5); //10
        // 对比执行remove事件前后的事件列表内容
        Observer.check();
        Observer.remove('eventName',fn);
        observer.check();
    }
    

    观察者模式在解决类的耦合中的应用小例子。想象一下课堂上老师提问,学生回答问题的场景。老师提问的问题名称相当于发出的事件,如果学生听到这个问题(事先注册了这个问题名称),那么便会回答(响应事件的动作)。

    学生类:

    let Student = function (result) {
        // 问题答案
        this.result = result;
        // 回答的动作
        this.answer = ()=>{
            console.log('答案',this.result);
        }   
    }
    Student.prototype.listen = function(question){
        Observer.register(question,this.answer);
    }
    Student.prototype.sleep = function (question) {
        Observer.remove(question,this.answer);
        console.log('这个问题我听不到听不到~');
    }
    

    教师类:

    let Teacher = function (){}
    // 对某一个问题发出问题
    Teacher.prototype.ask = function (question) {
        Observer.issue(question);
    }
    

    执行:

    let student1 = new Student('学生1回答问题');
    let student2 = new Student('学生2回答问题');
    let student3 = new Student('学生3:呼噜呼噜~');
    student1.listen('鸡生蛋还是蛋生鸡?');
    student2.listen('鸡生蛋还是蛋生鸡?');
    student3.listen('鸡生蛋还是蛋生鸡?');
    let teacher = new Teacher();
    // 老师发问
    function test () {
        teacher.ask('鸡生蛋还是蛋生鸡?');
    }
    //答案 学生1回答问题
    //es5观察者.html:68 答案 学生2回答问题
    //es5观察者.html:68 答案 学生3:呼噜呼噜~
    

    相关文章

      网友评论

        本文标题:javascript设计模式之观察者模式

        本文链接:https://www.haomeiwen.com/subject/ttlteqtx.html