AS3中的事件机制

看过很多人讲过AS3中的事件机制,一般讲AS3的书也都会专门开出一章讲事件机制。个人认为其中数N神讲的是最不错的。
不过别人归别人,我想对于事件机制懂的人都有相同的理解,不过表述肯定是不同的。
不说废话了。现在就开始说说事件机制。
先说说表层的东西。AS3中一般做一个事件侦听通常需要做的事情是:
A.addEventListener(MouseEvent.CLICK,onClick);
function onClick(e:MouseEvent):void{
//你要执行的代码
}
然后运行,点击A。你要执行的代码就会执行了。
很容易可以看出,第一个参数是表示注册要发生的事件(这里是鼠标单击事件),第二个参数是对应的响应这个事件的动作,就是要执行的函数,我们可以看到在这个响应函数的定义里面有一个参数e.这就是事件侦听器函数的特征了,就是说事件侦听函数在执行的时候会自动接收到一个参数,这个参数就是触发这个函数的事件。
前两个参数就是这样的意思,但是我们查查帮助文档可以发现,addEventListener这个方法函数可不只有两个参数,这两个参数是必须的,还有三个是非必须的,下面我们分析一下这三个非必须的参数。
先摘抄一下帮助文档的内容:
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
参数 type:String — 事件的类型。 listener:Function — 处理事件的侦听器函数。 此函数必须接受 Event 对象作为其唯一的参数,并且不能返回任何结果,如以下示例所示:
function(evt:Event):void
函数可以有任何名称。
useCapture:Boolean (default = false) — 确定侦听器是运行于捕获阶段、目标阶段还是冒泡阶段。 如果将 useCapture 设置为 true,则侦听器只在捕获阶段处理事件,而不在目标或冒泡阶段处理事件。 如果 useCapture 为 false,则侦听器只在目标或冒泡阶段处理事件。 要在所有三个阶段都侦听事件,请调用两次 addEventListener,一次将 useCapture 设置为 true,第二次再将 useCapture 设置为 false。 priority:int (default = 0) — 事件侦听器的优先级。 优先级由一个带符号的 32 位整数指定。 数字越大,优先级越高。 优先级为 n 的所有侦听器会在优先级为 n -1 的侦听器之前得到处理。 如果两个或更多个侦听器共享相同的优先级,则按照它们的添加顺序进行处理。 默认优先级为 0。 useWeakReference:Boolean (default = false) — 确定对侦听器的引用是强引用,还是弱引用。 强引用(默认值)可防止您的侦听器被当作垃圾回收。 弱引用则没有此作用。
 
//摘抄完毕
OK。从容易到困难,我们先看第四个参数,就是priority。。这个优先级应该是很好理解了,就是优先级高的先执行,低的后执行,数字大的为高,小的为低。默认的都是0。也就是说按定义先后执行。比如:
A.addEventListener(MouseEvent.CLICK,onClick1);
A.addEventListener(MouseEvent.CLICK,onClick2);
那么默认的,在点A的时候就会是onClick1执行,onClick2执行。这样的顺序。
如果是:
A.addEventListener(MouseEvent.CLICK,onClick1,false,1);
A.addEventListener(MouseEvent.CLICK,onClick2,false,0);
这样的话,在点A的时候就会是onClick2执行,onClick1执行。这样的顺序。
然后再看第三个参数:useCapture
从定义可以看出,这个参数是一个Boolean类型,就是true OR false。而且默认不写的话是false.
他主要是控制我们的侦听是发生在捕获阶段、目标阶段和冒泡阶段中的哪个阶段的。是不是有点懵。没关系,下面我们先讲讲这三个阶段。
 
 
上面是两个图,左边一个是说明一种层次关系,就是舞台上有一个东东,东东里面又有两个小东东。
第二个图是说如果小东东一被点击的话会出现的现像。我们知道addEventListener只是添加一个事件侦听,也就是说你做不做这个工作,只要你点击了元件,事件流是客观存在的。下面我们就第二个图看看这个客观存在是什么样的。
可以看见的有三个箭头,第一个Capture Phase(捕获阶段)第二个Target Phase(目标阶段)第三个Bubbing Phase(冒泡阶段)。
什么意思呢,就是说你点了Child1这个东东。那么事件会从舞台开始,往下到你点的东东,再向上到舞台.明白一点说,就是首先系统会认为你点击了舞台,然后是点击了Parent,然后是点击了你的目标Child1.然后再回上去。其中事件从stage跑到Child1之前,叫做捕获阶段,到达Child1叫做目标阶段(真正点击的目标),再回上去叫做冒泡阶段(很形像吧)。
三个阶段搞清楚了吧。那你是不是对这个Capture,捕获阶段这个单词有点眼熟呢,不错就是第三个参数里面useCapture.(终于又绕回来了)。现在想想,我们为Parent上注册了一个事件,其实就相当于在他上面放了一只耳朵,用来听听事件是不是经过了那里,经过就执行函数,那么三个阶段下来,事件是不是两次都经过了它,那耳朵是不是听到了两次事件。那函数不是要执行两次??这个问题的解决,就是应用useCapture这个参数,这个参数在默认为false表示,事件只能在目标阶段和冒泡阶段被侦听到。为true的话表示只能在捕获阶段被侦听到。
下面看几个例子(抄自N神的例子):
例1:
parentNode.addEventListener(MouseEvent.CLICK,func)
function func(e:MouseEvent):void{
trace(e.target.name,”——“,e.currentTarget.name)
trace(“e.eventPhase”,e.eventPhase)
}
/*———————-out put———-
mc1 —— mc
e.eventPhase 3
—————————————*/
扫盲:e.target表示的是你确实点的那个东西。比如上面说的Child1.
e.currentTarget表示的是你注册侦听器的那个东西,这里是parentNode这个元件。
e.eventPhase表示的是事件的侦听发生在哪个阶段,1,2,3分别表示捕获,目标,冒泡。这里输出3,表示是在冒泡的时候听到的,你看看那个图,应该是对吧,一共经过parent两次,因为useCapturefalse,所以第一次忽略,第二次是在冒泡的时候听到的,所以这里是3
我想讲到这里事件流其实就差不多了,后面你可以自己想想,怎么在三个阶段都起作用(用一个侦听肯定是不行的,因为只能侦听其中的一些阶段)。
下面再讲两个比较有意思的方法函数:e.stopPropagation()e.stopImmediatePropagation().
我们可以想像,只要我们写足够多的addEventListener.那么我们可以给一个目标和一个事件注册多个侦听函数。
举个例子:我们宿舍六个人,五个人去上课了,老大到老五都听到了老师说的作业的范围,回到宿舍,如果老大跟老六说了作业范围,是不是其它四个人就不用说了,如果老大没说,老二再说,其它人就不用说了,反正就是只要一个人跟老六说了作业了,其它人都不用再跟他说了。
这样的话相当于老六注册了五个侦听器,事件是老师布置作业,函数分别是老大告诉老六,老二告诉老六….。。。这样几个侦听器同时侦听一个事件那如果要互相管理(我说了你就不用说了)怎么办呢。那就要用到上面说的两个函数了。
这两个方法都是放在事件处理函数里面的
第一个的意思是说,注册给我事件的结点上的事件发生了之后,事件就不用跑了,所以以后的侦听就没用了,比如在Child1的侦听函数上写了这个。那么在parentNode上绑定的事件就不会发生了。
第二个的更强硬,说如果我听到了这个事件,那不仅是后续节点听不到事件了,就连现在这个节点上注册的其它函数也不能执行了。这个就类似刚才说的宿舍里的事。
好了,下面是最后一个参数useWeakReference,再坚持一下就成功了。
useWeakReference.意思是说是否用弱引用??什么叫弱引用??什么叫引用??
我们知道在面向对象的编程中,一切元素都是类的实例。比如前面说的Parent,Child1,Child2.这些,但是实例存在了我们得给他起了名不是,就像人一样,我们又知道,一个人,在家可能叫儿子就是他,在学校可能叫班长就是他,在网络上可能叫iammutex就是他。也就是说一个实例可能会有多个名字,我们称这些名字引用了实例,当然名字实际就是变量。
FlashPlayer的运作中,我们每次用new或者表达式增加一个实例。内存就被多占用一点,直到我们删除它,但是单单是靠人自己来做是远远不够的,所以FlashPlayer给我们帮了忙,他自己做了一个垃圾回收,就是说他觉得没用的东西,他就会从内存中给清空。等等,他这也太强盗了吧,我要不要这东西他怎么知道?他当然没有那么智能,但是他至少知道,如果有一个实例,如果没有变量去引用它,那么你肯定就永远都不可能用到它了,因为他一个名字都没有,你怎么叫他他都不会出来了。这就是垃圾回收机制。
所以第一个实例都有一个引用数,就是说有几个变量引用了它,如果引用数变为0了,那可以说他是垃圾了。然后删除他占用的内存。比如:
var A=new myClass();//实例有了一个名字,引用数为1
var B=A;//实例有了两个名字,引用数为2
A=11;//实例的一个名字被别人拿去了,引用数为1
B=22;//实例的最后一个名字都没有了,引用数为0.变成所谓的垃圾,被从内存删除
那么可能有一种情况。
A.valueB=B;
B.valueA=A;
A = 11;
B = 11;
这样的话,实例1实例2内在都保存有一个指向对方的引用。那么他们的引用数永远都不会为0.但是我们确实又不能再用他们了,这样的情况怎么办呢?
当然有办法,那就是另一种垃圾回收方法,FlashPlayer把所有的实例以引用的关系组成一个大大的网状。当然,我们知道上面的AB肯定是在网外面的另外一处了,这时大网之外的东西也叫被当作垃圾,回收!是不是很强大。呵呵。
讲了这么多垃圾,那我们的第四个参数是什么意思呢??
第四个用于指定是否为弱引用,也就是说你可以指定它为弱引用。如果注册侦听的时候不是弱引用的话,那么就算是注册事件的实例A被删除了,那么这个侦听还在,就是说他还会一直听,这样就不好了对吧。如果申明为弱引用的话,那如果A被删除了,那这个侦听的关系也同时被删除,侦听不会再继续下去了。
注意:有的人认为弱引用的话注册在侦听器里的函数也被回收,那是不对的。弱引用,被回收,只是说侦听这件事不再干了。
来自 http://hi.baidu.com/iammutex/item/553aab20b4caeb11087508f4

发表评论