优化代码--事件的处理

来源:互联网 发布:天注定 知乎 编辑:程序博客网 时间:2024/06/09 23:16

        事件绑定之后,会一直存在于内存中,而每一个绑定的事件,都会建立一个指向事件处理函数的连接,这种连接如果过多,会大大降低代码的质量,给浏览器很大的压力。所以,如何更优处理事件绑定,也是代码优化的一个重要环节--也就是事件委托。

       本篇文章内容主要参考:“编写可维护的JavaScript”和“JavaScript高级程序设计(第三版)”。

       我们都知道,当绑定的事件触发时,事件对象(event对象)会作为回调参数传入事件处理程序中,event对象中包含所有和事件相关的信息,包括:事件的宿主(target)以及事件类型等相关的信息,鼠标点击信息,键盘按键信息等。事件的优化中,事件委托就是基于event对象的信息进行定位,做出不同的逻辑,实现事件委托功能的。


一:事件的三种绑定方式:

   1:直接嵌入到标签中的绑定方式(这个因为违背了松耦合的思想,是最差的绑定方法,不多说)。
   2:直接对象的绑定,代码如下:
 
   function callback(){//code   }   document.getElementById(id).onclick = callback;


        这种方法大家应该很熟悉了吧,绑定事件的方法,如果需要用到event对象,那么这种写法是否依然可用?怎么说呢,你依然可以这么写,但是,在某些情况下,这会是错误的。
   
         为了避免这个错误,有时候就不得不使用匿名函数了,比如这么写(当然,回调函数的event是可以省略的)
   document.getElementById(id).onclick = function(event){callback(event);};

       至于原因呢,其实就是this导致的,具体在下一个小节“event的使用”中,进行说明。

       说的这里,你会不会想到一个事件优化方面的问题?匿名函数在不需要的时候,是无法被清理的,这样就会导致这个匿名函数会一直存在,影响浏览器的性能。
   

        对于这种绑定事件的方法,匿名函数也是可以被清理的,直接写

document.getElementById(id).onclick = null;

       这样就把目标上的onclick事件给清理了,当然,是全部被清理了,不过这有什么关系呢,本来这种事件绑定方法就只能绑定一个回调函数嘛,清理就全部清理呗。
   

        我觉得,这也是这种方法依然被人们认可一个因素吧,否则,这种方法除了看起来简单明了、所有浏览器支持(这个是最重要的原因吧)之外,我实在是想不到,为什么还在用这个方法了。

        这些个说法都是我自己的想法,仅供参考。
   3:dom绑定事件的方法:
   
function addEvent(node,type,callback){if(!node){return;}if(node.addEventListener){//W3Cnode.addEventListener(type,callback,false);}else if(node.attachEvent){//IE8-node.attachEvent("on"+type,callback);}else{//othersnode["on"+type] = callback;}}

这种方法的特点:

1:可以绑定多个事件,在支持W3C的浏览器下,多个事件是顺序执行的,在IE8-的浏览器中,是逆序执行的。

2:如果需要event对象,有的时候,也是需要使用匿名函数进行回调的,即:callback为匿名函数。使用方法和第二种事件绑定方法一致。原因也是一致的。

3:匿名函数不可被移除。

4:为了使得各浏览器的表现一致,都使用了冒泡的方式。

5:在回调函数中的this是有区别的,W3C的浏览器,this就是触发这个事件的元素本身。表现在上述的封装函数中,this=node;在ie8-中,this表现为addEvent函数调用所在的作用域,如果是在全局作用域中使用的,那么此时的this=window。所以在这种方法的事件绑定时,要注意this的引用。

二:event的使用

        如果你想要看看event对象,到底包含了哪些属性或者方法,只需要在新版本的浏览器(别用ie,ie不给显示的)中执行:Object.getOwnPropertyNames(event);

       下面这段代码,是实现被点击的元素,背景色改变为一个随机的颜色。
   
<html><head><style><body><div id = "a" onclick = "myClick.handleClick()">aaaaaaaaa</div><div id = "b">bbbbbbbbb</div><div id = "c">ccccccccc</div></body><script>//写法 1function myChangeColor(event){event = event || window.event;var color = Math.round(Math.random()*1000000),    target = event.target || event.srcElement;target.style.backgroundColor = color;}document.getElementById("c").onclick = myChangeColor;//写法 2var myClick = {handleClick:function(event){event = this.getEvent(event);this.changeColor(event);},getEvent:function(event){return event || window.event;},getRD:function(){return Math.round(Math.random()*1000000);},getTarget:function(event){return event.target || event.srcElement;},changeColor:function(event){var color = this.getRD(),    target = this.getTarget(event);target.style.backgroundColor = color;}};document.getElementById("b").onclick = function(event){myClick.handleClick()};</script></html>

        这里,我用两种写法实现了改变被点击元素背景色的功能模块。

        写法1:看起来更简单,不过,因为这个写法是把所有的东西都揉合在了一起,在功能模块单一且简单的情况下,是很简单的,可是,如果模块逻辑很复杂了之后呢?也许刚开始你写的时候,你本人逻辑很明确,功能实现也很好,可是,当需求变更之后呢?当你去负责其他的项目,这个模块由其他人维护的时候出现问题的时候呢,它要花多少时间去理解你当时的逻辑,然后才能做出修改?这种写法,使得在需求变更时,维护更加困难。


       写法2:虽然现在写的代码较多,但逻辑很明确,就算是之后需求变更,我可以跟着那个绑定的接口,一步一步的调试,这样就很容易能确认问题的所在,并且在添加功能或者修改逻辑时,也更容易在哪里修改会更优,而且不需要担心是否会影响到其他地方的逻辑。这样大大的降低了维护人员的工作量,提升了团队的工作效率。这也是敏捷开发的要求,也是设计模式出现的原因之一。

       说多了,要说event的使用呢。

        写法 1:我绑定到了id=“c”的div,我们常用的写法,没有问题。

        写法 2:我这里用了个匿名函数,为什么不直接绑定呢,就像这样

  document.getElementById("b").onclick = myClick.handleClick;

这就是因为函数的本质原因了,函数就是一个指针,如上面的写法,其实就是把myClick.handleClick指向的代码以匿名函数的方式,赋值这个onclick事件,代码的表示就更明确了。


document.getElementById("b").onclick = function(event){event = this.getEvent(event);this.changeColor(event);};

就是这样,想想这个时候,this代表了什么呢?很明显,this代表的是,id="b"的这个div的DOM对象,可是这个DOM对象并没有changeColor方法,所以这个地方就会出现错误。

而以匿名函数的写法呢,
document.getElementById("b").onclick = function(event){//这里的this代表什么呢?var that = this;myClick.handleClick();};

这个时候,myClick.handleClick(),就是一个局部作用域了,在这个对象内部的this,就是代表了对象myClick本身了,myClick对象内部的this和上面函数内定义的that,其实就是不同的了,这个地方的that,也是代表id="b"的这个div的DOM对象。

这就是为什么,有些事件绑定方法,必须要用匿名函数的原因,其实质,就是作用域的问题,this的问题。

所以呢,如果你想要避免这些个问题,也很简单,抛去this不用即可,比如把写法 2做如下改动:
var myClick = {handleClick:function(event){event = myClick.getEvent(event);myClick.changeColor(event);},getEvent:function(event){return event || window.event;},getRD:function(){return Math.round(Math.random()*1000000);},getTarget:function(event){return event.target || event.srcElement;},changeColor:function(event){var color = myClick.getRD(),    target = myClick.getTarget(event);target.style.backgroundColor = color;}};document.getElementById("b").onclick = myClick.handleClick;

这个时候,就可以直接这样绑定了,因为,没有用到this,没有作用域的问题了。

        至于刚开始说到的,减少事件绑定数目的事件委托,就只加一个例子吧,代码也就是和以上代码相差不大。

<html><head><style></style></head><body><div  id = "a"><div>aaaaaaaaa</div><div>bbbbbbbbb</div><div>ccccccccc</div></div></body><script>var myClick = {handleClick:function(event){event = myClick.getEvent(event);myClick.changeColor(event);},getEvent:function(event){return event || window.event;},getRD:function(event){return Math.round(Math.random()*1000000);},getTarget:function(event){return event.target || event.srcElement;},changeColor:function(event){var color = myClick.getRD(event),    target = myClick.getTarget(event);target.style.backgroundColor = color;}};document.getElementById("a").onclick = myClick.handleClick;</script></html>

        变动呢,首先,是html部分变动了,其次,是js部分,没有绑定需要点击的div,而是绑定了它们的父元素,这样做的好处呢,就是,把事件绑定的个数减少了,再者,以后如果再次添加兄弟元素,那么也不需要再次绑定事件了。

       上述代码中,只是简单的说明了事件委托的实现方式,并没有做各种其他的处理,事件委托中,绑定事件的逻辑中,其实是很复杂的,有兴趣的朋友可以去查看事件委托方面的资料,或者再自己的项目中,使用一下,很不错的哦。


        感谢查看,如果您有任何问题,或者发现文中的错误,或者描述不当,欢迎交流。
1 0
原创粉丝点击