zoukankan      html  css  js  c++  java
  • Javascript之自定义事件

    Javascript自定义事件,其本质就是观察者模式(又称订阅/发布模式),它的好处就是将绑定事件和触发事件相互隔离开,并且可以动态的添加、删除事件。

    下面通过实例,一步一步构建一个具体的Javascript自定义事件对象。

    如:我有一个action1函数,我想每次在执行完action1后,触发另一个函数service1,那么代码我们可以这么写:

    //服务service1
    function service1(){
    
    }
    //函数action1
    function action1(){
        //other things
        //then 启动service1
        service1();
    }

    Good,但是现在想法变了,我想在action1完成后,不仅触发service1,还要触发service2和service3。

    按照刚才的思路,在函数action1完成后,顺带加上它们就是了。

    如下:

    function service1(){}
    function service2(){}
    function service3(){}
    
    function action1(){
        //other things 
        service1();
        service2();
        service3();
    }

    但,想法又再次发生波动,在执行完action1函数后,我突然想动态添加一个service4,且,发现service2似乎毫无意义,我不想触发了,怎么办呢?

    你可能会说去掉service2,然后在action1后面加入service4不就完了吗?

    但是,在真正的项目开发代码日益剧增的情况下,谈何容易,还要去找到相关代码进行操作。

    那怎么办呢?

    初步想法,定义一个数组嘛(如:servicearray),用来管理所有的service。

    当action1执行到末尾后,遍历一遍这个数组函数(servicearray),就欧克了嘛。

    且,倘若我们不想运行service2了,便将它从这个数组中删除就好了;倘若想再添加一个新的service,将其追加到servicearray数组中就好了。

    如此nice,如下:

    var servicearray = [];
    
    function service1(){}
    function service2(){}
    function service3(){}
    //将所有service添加到servicearray中
    servicearray.push(service1);
    servicearray.push(service2);
    servicearray.push(service3);
    //del:用于删除一个指定的service
    function del(arr, fn){
        for(var i = 0; i < arr.length; i++){
            if( arr[i] == fn ){
                arr.splice(i,1);
                break;
            }
        } 
    }
    //action1后,执行所有的service
    function action1(){
        //other things 
        //遍历serviceaary,执行所有service函数。(servicearray在action1内)
        for(var i =0; i < servicearray.length; i++){
            servicearray[i]();
        }
    }
    //添加service4
    function service4(){}
    servicearray.push(service4);
    //删除service2
    del(servicearray, service2);

    上面代码挺欧克的,但,复用性一点都不强,且servicearray与action你中有我,我中有你,不好。我们再来优化优化。

    代码如下:

    var servicearray = [];
    
    function service1(){}
    function service2(){}
    function service3(){}
    
    servicearray.push(service1);
    servicearray.push(service2);
    servicearray.push(service3);
    function del(arr, fn){
        for(var i = 0; i < arr.length; i++){
            if( arr[i] == fn ){
                arr.splice(i,1);
                break;
            }
        } 
    }
    //添加一个service4
    function service4(){}
    servicearray.push(service4);
    //删除一个service2
    del(servicearray, service2);
    //添加一个触发函数hanldeAction,分离action与service
    function hanldeAction(actionName,serviceArr){
        if(typeof actionName === 'function'){
            actionName();
            for(var i =0; i < serviceArr.length; i++){
                serviceArr[i]();
            }
        }
    }
    //执行
    handleAction(action1,servicearray); 

    上面的代码和回调函数有异曲同工之处,因为我们想达到的效果是在 action执行完成之后,运行一系列service嘛。

    但,我现在改变想法了,我想在action执行之前执行一系列service呢,或者action中呢。看来得改hanldeAction回调函数啊,这放在项目中反复修改,显然不行。

    所以,我们得让其更强大才好。(我想让它在什么地方执行就执行)

    如下:

    function service1(){}
    function service2(){}
    function service3(){}
    
    var servicearray = [];
    servicearray.push(service1);
    servicearray.push(service2);
    servicearray.push(service3);
    function del(arr, fn){
        for(var i = 0; i < arr.length; i++){
            if( arr[i] == fn ){
                arr.splice(i,1);
                break;
            }
        } 
    }
    //添加一个service4
    function service4(){}
    servicearray.push(service4);
    //删除一个service2
    del(servicearray, service2);
    /*
        actionObj用于存储所有action与service关联的对象。
        如:{
                action1:[service1,service2],
                action2:[...]
            }
    */
    var actionObj = {};
    /*
        修改代码,增加一个actionName与serviceArr关联事件。
        如,action1关联所有service,这样再结合下方的trigger事件就完美了
        Params:
               actionName --> actionObj的属性
               serviceArr --> 包含所有与actionName相关的service数组
    */
    function addAction(actionName, serviceArr){
        if(typeof actionObj[actionName] === 'undefined' ){
            actionObj[actionName] = [];
        }
        if(typeof serviceArr === 'object'){
            actionObj[actionName].push(serviceArr);
        }
    }
    /*
        修改代码,增加一个触发actionName事件
        如,当我想触发action1中的所有service时,调用trigger(action1)就OK啦
    */
    function trigger( actionName ){
        var act = actionObj[actionName];
        if(act instanceof Array){
            for(var i = 0, len = act.length; i < len; i++){
                for(var j =0, arrlen = act[i].length; j++){
                    ((act[i])[j])();
                }
            }
        }
    }

    上述代码是可以,但,有个性能问题,addAction中添加到actionObj[actionName]中的是一个数组,其实完全可以将定义的servicearray数组(为了存储不同的service而声明的数组)移除,转而将每个service直接push进actionObj[actionName]声明的数组中,这样trigger事件效率也得到了提高,从原来的两层for循环降到一层for循环。且,我们再加一个删除service的方法remove。

    整理代码如下:

    var actionObj = {};
    //修改代码,增加一个actionName与service函数直接关联事件
    function addAction(actionName, fn){
        if(typeof actionObj[actionName] === 'undefined' ){
            actionObj[actionName] = [];
        }
        if(typeof fn === 'function'){
            actionObj[actionName].push(fn);
        }
    }
    //修改代码,增加一个触发actionName事件
    function trigger( actionName ){
        var actionarray = actionObj[actionName];
        if(actionarray instanceof Array){
            for(var i = 0, len = actionarray.length; i < len; i++){
                if(typeof actionarray[i] === 'function'){
                    actionarray[i]();
                }
            }
        }
    }
    //修改代码,增加一个删除actionName中的service事件
    function remove(actionName, fn){
        var actionarray = actionObj[actionName];
        if(typeof actionName === 'string' && actionarray instanceof Array){
            if(typeof fn === 'function'){
                //清除actionName中对应的fn方法
                for(var i=0, len = actionarray.length; i < len; i++){
                    if(actionarray[i] === fn){
                        actionObj[actionName].splice(i,1);
                    }
                }
            }
        }
    }

    上面的代码好是好,action与service也互不影响,也完成了它的使命。

    使命?

    这就是我们一起编写的自定义事件嘛。是不是很简单。

    哈哈哈,我尼玛也在代码中用到设计模式了(观察者模式)。

    一鼓作气,我们再来优化下上面的代码。有没有注意,我们是使用的全局变量,在模块化开发的大环境下,我们居然在用全局变量,岂不是污染命名空间嘛。再改改。

    修改代码如下:

    var EventTarget = function(){
        this.listener = {};
    }
    EventTarget.prototype = {
        constructor:EventTarget,
        addAction: function(actionName, fn){
            if(typeof actionName === 'string' && typeof fn === 'function'){
                //如果不存在actionName,就新建一个
                if(typeof this.listener[actionName] === 'undefined'){
                    this.listener[actionName] = [fn];
                }
                //否则,直接往相应actinoName里面塞
                else{
                    this.listener[actionName].push(fn);
                }
            }
        },
        trigger: function(actionName){
            var actionArray = this.listener[actionName];
            //触发一系列actionName里的函数
            if(actionArray instanceof Array){
                for(var i = 0, len = actionArray.length; i < len; i++){
                    if(typeof actionArray[i] === 'function'){
                        actionArray[i]();
                    }
                }   
            }
            actionArray = null;
        },
        remove: function(actionName, fn){
            var actionArray = this.listener[actionName];
            if(typeof actionName === 'string' && actionArray instanceof Array){
                if(typeof fn === 'function'){
                    //清除actionName中对应的fn方法
                    for(var i=0, len = actionArray.length; i < len; i++){
                        if(actionArray[i] === fn){
                            this.listener[actionName].splice(i,1);
                        }
                    }
                }
            }
            actionArray = null;
        }
    };

    一个JavaScript自定义事件新鲜出炉。

    好了,晚安everyone~

  • 相关阅读:
    三行Python代码查询IP
    剑指offer面试题29:数组中出现次数超过一半的数字
    【简】题解 AWSL090429 【数塔问题】
    Re.常系数齐次递推
    Re.多项式除法/取模
    【翻译】A simple stone game
    Re.多项式求逆
    Re.FFT
    题解 P4783 【【模板】矩阵求逆】
    关于win10企业版在极域电子教室软件 v4.0 2015 豪华版的全屏控制下如何取得自由
  • 原文地址:https://www.cnblogs.com/giggle/p/5380832.html
Copyright © 2011-2022 走看看