javascript设计模式之观察者模式

栏目: JavaScript · 发布时间: 6年前

内容简介:这篇笔记主要记录学习思路及收获,分享出来抛砖引玉,如有谬误或优化空间,欢迎交流。要理解观察者模式,可以类比vue中的基于上面的思路,首先要有一个对象,它有一个私有的列表属性和对外暴露的两个方法

这篇笔记主要记录学习思路及收获,分享出来抛砖引玉,如有谬误或优化空间,欢迎交流。

要理解观察者模式,可以类比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:呼噜呼噜~

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Host Your Web Site In The Cloud

Host Your Web Site In The Cloud

Jeff Barr / SitePoint / 2010-9-28 / USD 39.95

Host Your Web Site On The Cloud is the OFFICIAL step-by-step guide to this revolutionary approach to hosting and managing your websites and applications, authored by Amazon's very own Jeffrey Barr. "H......一起来看看 《Host Your Web Site In The Cloud》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码