ActionScript 3.0, использующий замыкания для обработчиков событий - PullRequest
15 голосов
/ 07 октября 2008

Я пытался сделать это:

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 ]

36 голосов
/ 07 октября 2008

Вот общий способ удаления прослушивателей событий, которые я использовал в производственных проектах


addEventListener
(
    Event.ACTIVATE, 
    function(event:Event):void
    {
        (event.target as EventDispatcher).removeEventListener(event.type, arguments.callee)             
    }
)
7 голосов
/ 09 октября 2010

Как уже было предложено, вы можете удалить замыкание из цепочки слушателей изнутри самого замыкания. Это делается с помощью arguments.callee:

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

    // Whatever else needs doing goes here
});

Это эффективно превратит замыкание в одноразового прослушивателя события, просто отсоединив себя после срабатывания события. Хотя это синтаксически многословно, это невероятно полезный метод для тех многих событий, которые действительно запускаются только один раз (или вас интересует только один раз), например, «creationComplete» во Flex. Я использую это все время при загрузке данных, так как думаю, что встроенный код обратного вызова облегчает понимание. Это похоже на сокрытие асинхронности:

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);

Очевидно, что это абстрактный пример, но использование таких замыканий может быть довольно полезным. Однако у них есть недостатки, например, они менее эффективны, чем именованные методы. Другим недостатком является тот факт, что вам действительно нужно хранить ссылку на закрытие, если оно вам когда-нибудь понадобится. Затем необходимо позаботиться о сохранении целостности этой ссылки, как и с любой другой переменной.

Таким образом, хотя разный синтаксис может иметь свое применение, это не всегда лучшее решение. Это яблоки и апельсины.

3 голосов
/ 07 октября 2008

Вы можете рассматривать ключевое слово function () как конструктор, каждый раз создавая новый объект (замыкание). Поэтому, если вы создаете замыкание только для параметра и нигде не сохраняете ссылку, нет способа получить «то же самое» замыкание где-либо еще.

Очевидным решением является то, что вам не нравится, определение функции перед ее использованием. Конечно, это все еще может быть полное закрытие, а не просто «статическая» функция. просто определите его в нужном вам контексте и назначьте его локальной переменной.

2 голосов
/ 07 июля 2010

Я использую это иногда:

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

root.removeEventListener("click", closure);
0 голосов
/ 04 июня 2009

Я часто этим занимался, поэтому попробовал. Кажется, работает нормально.

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);
        })
}
0 голосов
/ 18 октября 2008

Я не знаю, что вы на самом деле делаете, но в этом конкретном примере, возможно, у вас может быть глобальная переменная _clickEnabled.

Затем внутри обработчика событий вы просто проверяете _clickEnabled, а если его значение ложно, вы просто return немедленно.

Затем вы можете включать и отключать общее событие, не отсоединяя и не подключая его.

0 голосов
/ 10 октября 2008

Просто примечание к вашему коду, которое я обнаружил в наборе учебных пособий Flex In A Week на веб-сайте Adobe. Там они сказали, что вы всегда должны использовать константы для типов событий, а не для строк. Таким образом вы получаете защиту от опечаток. Если вы сделаете опечатку в строке типа события (например, скажем «clse»), ваш обработчик событий будет зарегистрирован, но, конечно, никогда не будет вызван. Вместо этого используйте Event.CLOSE, чтобы компилятор ловил опечатку.

0 голосов
/ 08 октября 2008

Это не слишком сильно отличается от использования определенной функции, но, возможно, это удовлетворит ваши потребности. Помните, что функции являются первоклассными объектами в 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
0 голосов
/ 07 октября 2008

Я не уверен, что это сработает, но стоит попробовать:

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

Более подробную информацию об этом можно найти Flex lang ref

...