观察者模式(自定义事件)

开篇导读:在js中接触了内置的事件监听器,比如我们绑定了按钮的一个点击事件及赋予它一个事件处理函数,当点击按钮时便触发了这个内置监听器也就是onclick,于是开始执行我们赋予它的处理函数,由此我们会想到这种事件监听器我们自己可不可以定义呢?其实我们可以模拟内置事件的监听和触发过程,以此来达到我们需要的效果,在行业领域当中,它被称为观察者模式,直接上实现代码

//定义一个对象
let ObserverMode = {
        //定义添加自定义事件
        on: function(){
            // 判断参数是否满足条件,不满足则返回"参数错误"
            if(!arguments || arguments.length < 2) return this.warningMessages.info1;
            
            // 取到第一个参数作为自定义事件名
            let observes = arguments[0];
            
            // 判断第一个参数类型是否符合整数或字符串,不满足则返回"第一个参数不符合字符或数值类型"
            if(!this.isFistType(observes)) return this.warningMessages.info2;
            
            // 取到第二个参数作为自定义事件处理函数
            let fn = arguments[1];
            
            // 判断第二个参数类型,不满足则返回"第二个参数不是函数"
            if(!this.isSecondType(fn)) return this.warningMessages.info3;
            
            // 如果当前对象中不存在handler对象,也可以称为事件管理者,则创建对象
            if(!this.handler) this.handler = {};
            
            //如果事件管理者中不存在需要自定义事件的名称列表,则新建一个数组列表
            if(!this.handler[observes]) this.handler[observes] = [];
            
            //在对应的自定义事件名称列表新增这个事件处理函数
            this.handler[observes].push(fn);
        },
        // 触发自定义事件
        emit: function (){
            // 取到事件名
            let observes = arguments[0];
            //取到参数
            let params = arguments[1];
            // 如果传入的事件名未注册或列表为空则返回"没有注册该事件"
            if(!this.handler[observes] || !this.handler[observes].length) return this.warningMessages.info4;
            //否则执行这个事件名称列表中的每个函数
            else this.handler[observes].forEach(item =>{
                item(params);
            })
        },
        // 移除自定义事件
        off: function(){
            // 判断参数是否满足条件,不满足则返回"参数错误"
            if(!arguments || !arguments.length) return this.warningMessages.info1;
            
            // 取到事件名
            let observes = arguments[0];
            
            //如果参数长度大于零和第一个参数不符合类型则返回"第一个参数不符合字符或数值类型"
            if(arguments.length > 0 && !this.isFistType(observes)) return this.warningMessages.info2;
            
            //如果参数长度等于1和事件管理者中存在该事件名称则移除对应列表,不满足则打印"没有注册该事件"
            if(arguments.length == 1){
                if(Object.keys(this.handler).indexOf(observes) > -1){
                    delete this.handler[observes];return;
                } 
                else return this.warningMessages.info4;
            }
            
            //取到参数
            let fn = arguments[1];
            //如果参数长度大于1和第二个参数不符合类型则返回"第二个参数不是函数"
            if(arguments.length > 1 && !this.isSecondType(fn)) return this.warningMessages.info3;
            else {
                // 判断事件列表中是否存在指定的事件处理函数
                let index = this.handler[observes].indexOf(fn);
                //存在则移除对应的事件处理函数,否则打印"该事件列表没有找到对应的执行函数"
                index > -1?this.handler[observes].splice(index,1):console.log(this.warningMessages.info5);
            }
        },
        // 判断第一个参数类型是否符合整数或字符串的方法
        isFistType: function(value){
            let type = typeof value;
            return (type == 'number') || (type == 'string');
        },
        // 判断第二个参数类型
        isSecondType: function(value){
            return value.prototype && (value.__proto__ === Function.prototype)
        },
        //提示数据
        warningMessages: {
            info1: "参数错误",
            info2: "第一个参数不符合字符或数值类型",
            info3: "第二个参数不是函数",
            info4: "没有注册该事件",
            info5: "该事件列表没有找到对应的执行函数",
        }
}

到此自定义的事件就实现了,上面代码的功能和内置事件不同的是,它需要我们手动调用对象中的emit触发,内置事件的每个事件名称只对应一个执行函数,这里的自定义事件是一个事件列表,可以同时处理多个同名事件函数

posted @ 2020-04-11 12:01  Passer丶  阅读(287)  评论(0编辑  收藏  举报