ActionScript 3.0使用事件处理程序的闭包

时间:2008-10-07 18:06:10

标签: actionscript-3 events closures

我试过这样做:

root.addEventListener("click", 
   function () 
   { 
      navigateToURL(ClickURLRequest,"_self"); 
   });

它确实添加了事件监听器。我喜欢使用闭包,因为它们在这种情况下运行良好,

然而,删除事件监听器需要引用原始函数,因为我使用了匿名闭包,它不起作用,我试过了:

   root.removeEventListener("click", 
       function () 
       { 
          navigateToURL(ClickURLRequest,"_self"); 
       });

以及:

   root.removeEventListener("click", function () {} );

我发现它的唯一方法就是抛弃匿名闭包并将事件监听器指向一个预先存在的函数:

 function OnClick (e:Event)
 {
     navigateToURL(ClickURLRequest,"_self");
 }

 root.addEventListener("click", OnClick);
 root.removeEventListener("click", OnClick);

有没有人知道在事件处理程序中使用匿名闭包的方法,同时仍然保留删除它们的能力?

9 个答案:

答案 0 :(得分:36)

这是删除我在生产项目中使用的事件侦听器的一般方法


addEventListener
(
    Event.ACTIVATE, 
    function(event:Event):void
    {
        (event.target as EventDispatcher).removeEventListener(event.type, arguments.callee)             
    }
)

答案 1 :(得分:7)

正如已经提出的那样,你可以从闭包本身中删除闭包中的闭包。这是通过使用arguments.callee:

完成的
myDispatcher.addEventListener("click", function(event:Event):void
{
    IEventDispatcher(event.target).removeEventListener(event.type, arguments.callee);

    // Whatever else needs doing goes here
});

这将有效地将闭包转变为事件的一次性监听器,只需在事件触发后自行分离。虽然语法冗长,但对于那些真正只触发一次(或者你只关心一次)的事件来说,它是一种非常有用的技术,比如Flex中的“creationComplete”。我一直在下载数据时使用它,因为我认为内联回调代码使得它更容易理解。这就像隐藏了异步:

myLoader.addEventListener("complete", function(event:Event):void
{
    /* Even though the load is asynchronous, having the callback code inline
     * like this instead of scattered around makes it easier to understand,
     * in my opinion. */
});

但是,如果您想多次收听该事件,由于显而易见的原因,这将不会非常有效。在这种情况下,您需要在某处存储对闭包的引用。方法是ActionScript中的任何其他对象,可以传递。因此,我们可以将代码更改为:

var closure:Function;

myDispatcher.addEventListener("click", function(event:Event):void
{
    closure = arguments.callee;

    // Whatever else needs doing goes here
});

当您需要删除事件侦听器时,请使用'closure'引用,如下所示:

myDispatcher.removeEventListener("click", closure);

显然,这是一个抽象的例子,但使用这样的闭包非常有用。但它们确实有缺点,例如效率低于命名方法。另一个缺点是,如果您需要,您实际上必须存储对闭包的引用。必须注意保持该引用的完整性,就像使用任何其他变量一样。

因此,尽管不同的语法可能有其用途,但它并不总是最佳解决方案。这是一种苹果和橘子的东西。

答案 2 :(得分:3)

您可以将function()关键字视为构造函数,每次都创建一个新对象(闭包)。因此,如果您仅为参数创建闭包并且不在任何地方保留引用,则无法在其他位置保持“相同”闭包。

显而易见的解决方案是您不喜欢的,在使用之前定义该功能。当然,它仍然可以完全关闭,而不仅仅是“静态”功能。只需在您想要的上下文中定义它,并将其分配给局部变量。

答案 3 :(得分:2)

我有时会使用它:

var closure:Function = null;
root.addEventListener("click", 
   closure = function () 
   { 
      navigateToURL(ClickURLRequest,"_self"); 
   });

root.removeEventListener("click", closure);

答案 4 :(得分:0)

我不确定这是否有效,但值得一试:

root.removeEventListener("click", arguments.callee );

有关它的更多信息,请访问Flex lang ref

答案 5 :(得分:0)

与使用已定义的功能并没有太大的不同,但这可能会满足您的需求。请记住,函数是ActionScript中的第一类对象,您可以将它们存储并作为变量传递。

protected function addListener()
{
    m_handler = function(in_event:Event) { removeEventListener(MouseEvent.CLICK, m_handler); m_handler=null}
    addEventListener(MouseEvent.CLICK, m_handler)
}
protected var m_handler:Function

答案 6 :(得分:0)

我在Adobe网站上的Flex In A Week教程中遇到的代码的旁注。在那里,他们说你应该总是使用常量来表示事件类型而不是字符串。这样你就可以得到拼写错误的保护。如果你在事件类型字符串中输入拼写错误(比如说“clse”),你的事件处理程序将被注册但当然​​不会被调用。相反,使用Event.CLOSE以便编译器捕获错字。

答案 7 :(得分:0)

  

我发现自己做了很多,所以我尝试了这个。似乎工作正常。

addSelfDestructiveEventListener('roomRenderer', 'complete', trackAction, 'floorChanged');

private function addSelfDestructiveEventListener(listenee:*, event:String, functionToCall:Function, StringArgs:String):void
{
    this[listenee].addEventListener(event, function(event:Event):void
        {
            (event.target as EventDispatcher).removeEventListener(event.type, arguments.callee);
            functionToCall(StringArgs);
        })
}

答案 8 :(得分:-1)

我不知道你在做什么,但在这个特定的例子中,你可能有一个_clickEnabled全局变量。

然后在事件处理程序中你只需检查_clickEnabled,如果它是false,你只需立即return

然后,您可以启用和禁用整个事件,而无需分离和重新附加它。