js也可以有自定义事件 注入就是这么爽

  在c#中有delegate,还有特殊的可以直接应用于事件编程的delegate,那就是event。而在js中没有c#的event,更没有delegate,有的只是dom元素内置的的native的不可扩展的event,比如无法为input元素添加事件,只能在其拥有的事件(如onclick=handler)上扩展应用。那么能不能做到自定义的事件模拟效果呢?答案是肯定的,也就是本文的主题。

  首先弄明白一下事件的意图——可以在发生一件事的时候执行额外的代码,如document.attachEvent('onclick', function(){alert('u click document')}),当点击页面时(事件发生了),就会执行我们为其挂接的其它代码(js中以function为语句集合,以下称为function),当然我们可以在一个事件上挂接任意多的function,这样就实现了一种灵活的可扩展编程接口。试想如果可以像在元素事件扩展应用一样可以在任意对象的任意方法上扩展,那对于js编程来讲就更加灵活了。先看一个例子,平时我们把相对对立的一个功能命名为一个function,并在需要的地方(通常是另一个function)调用以实现代码复用:

function F(){
    this.method = function(){
        alert('f.method is called')
        g();
    }
}
function g(){
    alert(123)
}
var f = new F();
f.method()

我们把f.method中直接调用g改写一下,封装到一个Event对象中达到一样的效果,代码如下:

var Event = {
    __list:[],
    observe:function(obj, ev, fun){
        this.__list.push({o:obj, e:ev, f:fun})
    },
    occor:function(obj, method){
        var arr = []
        for(var i=0; i<this.__list.length; i++){
            if(this.__list[i].o==obj && this.__list[i].e==method) arr.push(this.__list[i]);
        }
        for(var i=0; i<arr.length; i++){
            arr[i].f();
        }
    }
}

function F(){
    this.method = function(){
        alert('f.method is called')
        Event.occor(this, 'method');
    }
}

var f = new F();
Event.observe(f, 'method', function(){alert(123)})
f.method()

这样乍看上去好像费了“太多”功夫,但却把“在f中调用g的写法”更通用化了,如果要在f中调用h则只需要多些一行Event.occor(this, 'methodName'),写到这里你肯定也注意到methodName的写法和最开始的写法是一样的,都是硬编的不具灵活性,如果在每个类的方法中都写入Event.occor(this, 'method')就太不雅观了,也背离了我们的初衷,动态修改一下method把它加到最后一行就ok了,下一步就是解决它,改进代码如下:

var Event = {
    __list:[],
    observe:function(obj, ev, fun){
        this.__list.push({o:obj, e:ev, f:fun})
    },
    occor:function(obj, method){
        var arr = []
        for(var i=0; i<this.__list.length; i++){
            if(this.__list[i].o==obj && this.__list[i].e==method) arr.push(this.__list[i]);
        }
        for(var i=0; i<arr.length; i++){
            arr[i].f();
        }
    },
    inject:function(obj){
        for(var p in obj){
            obj[p] = new Function(obj[p].toString().replace('function(){', '').replace('}', 'Event.occor(this,p)'))
        }
    }
}

function F(){
    this.method = function(){
        alert('f.method is called')
    }
}

var f = new F();
Event.inject(f);
Event.observe(f, 'method', function(){alert(123)})
f.method()

我们把显示的在被调用方法体内调用Event.occor改写到Event.inject中。到此我们就简单(还有一些安全代码没有处理,如没有判断obj[p]是否需要被改写、没有测试效率问题,没有处理更多添加Event.occor时的逻辑判断,下一步准备把它实现为一个Observeable对象,就更加灵活了)的完成了自定义事件。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏耕耘实录

几个Linux命令及脚本使用中的奇淫巧技

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

902
来自专栏丑胖侠

《Drools7.0.0.Final规则引擎教程》第4章 4.2 activation-group& dialect& date-effective

activation-group 该属性将若干个规则划分成一个组,统一命名。在执行的时候,具有相同activation-group 属性的规则中只要有一个被执行...

22210
来自专栏Java成神之路

rabbitmq_学习_00_资源帖

652
来自专栏Python绿色通道

Python的进程

Python实现多进程的方式主要有两种:一种方法是使用os模块中的fork方法; 另一种是使用multiprocessing模块。这两种方法的区别在于前者仅适用...

902
来自专栏CDN及云技术分享

GDB实现原理和使用范例

这篇文章为了让你深入了解gdb的工作原理,以及如何在linux环境下使用强大的gdb调试程序功能。

7341
来自专栏我爱编程

Day15进程和线程

多进程 multiprocessing multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结...

2845
来自专栏醒者呆

Debug EOS:nodeos + mongo_db_plugin

nodeos开始运行前,要先使用项目的总CmakeList.txt配置,这里我配置了boost库的位置,如果你配置了boost的环境变量可以跳过这里。

3601
来自专栏Linyb极客之路

工作流引擎之activiti任务监听器

任务监听器只能添加到流程定义中的用户任务中。 注意它必须定义在BPMN 2.0 extensionElements的子元素中, 并使用activiti命名空间,...

2462
来自专栏码洞

Spark通信原理之Python与JVM的交互

我们知道Spark平台是用Scala进行开发的,但是使用Spark的时候最流行的语言却不是Java和Scala,而是Python。原因当然是因为Python写代...

1021
来自专栏极客猴

os.path 模块用法详解

总所周知,Windows 操作系统和 Linux 系统存在很多不兼容的地方。文件路径就是一个明显的例子。在 Linux 中,路径的分割采用正斜杠 "/",比如 ...

903

扫码关注云+社区