Вопрос шаблона наблюдателя - объект JavaScript удаляет себя из родительского массива?

Я пытаюсь написать отношение объекта, которое следует шаблону наблюдателя, где наблюдатель заботится о конкретном наборе событий, происходящих с субъектом.

Я не уверен, что это стандарт на 100%, но в том виде, в котором я его построил, эти объекты событий определяются в наблюдателе с пользовательскими обратными вызовами, которые срабатывают при возникновении события. Когда на субъекте происходит событие, он проходит через всех своих наблюдателей и смотрит, кто наблюдает за этим событием. Если он находит наблюдателей, наблюдающих за этим событием, он запускает обратный вызов события наблюдателя.

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

Это кажется хорошим планом, но я знаю, что объект JavaScript не может вызывать метод delete() для самого себя.

Мне просто любопытно, кто-нибудь еще сталкивался с этим и нашел эффективное решение.

Моя единственная мысль заключалась в том, что я мог бы передать ссылку на родительский объект-наблюдатель, на его дочернее событие, а затем, когда произойдет обратный вызов, я мог бы вызвать метод внутри родителя... что-то вроде removeEvent(this) передачи самого события этой функции. Затем функция removeEvent может выделить событие из своего массива событий. Единственной сложной проблемой будет поиск местоположения этого объекта события в массиве. (принимаю предложения и по этому поводу, спасибо!).

Заранее спасибо за помощь!


person clockworked247    schedule 09.03.2011    source источник


Ответы (2)


Я думал, что смогу ответить на ваш вопрос, но я не уверен на 100%, как вы реализовали свой шаблон наблюдателя. Возможно, в моем фрагменте ниже есть что-то полезное. Я предположил, что наблюдение выполняется с помощью функций обратного вызова, и я ничего не предполагал о формате этих данных, поэтому я использую строки.
Суть моего решения заключается в том, что обратный вызов получает ссылку на Subject ( что обычно в шаблоне наблюдателя). Эту ссылку можно использовать для отделения обратного вызова от субъекта.

var Subject = {
    observers: {},

    attach: function( eventType, fn ) {
        if( !this.observers[eventType] ) this.observers[eventType] = [];
        this.observers[eventType].push( fn );
    },

    detach: function( fn ) {
        var newObservers,
            eventType,
            i;
        for( eventType in this.observers ) {
            newObservers = [];
            for( i = 0; i < this.observers[eventType].length; i++ ) {
                if( this.observers[eventType][i] !== fn ) newObservers.push( this.observers[eventType][i] );
            }
            this.observers[eventType] = newObservers;
        }
    },

    notify: function( eventType, data ) {
        var i, observers = this.observers[eventType].slice(0);
        for( i = 0; i < observers.length; i++ ) {
            observers[i]( data, this );
        }
    },

    poke: function() {
        this.notify( 'testing', 'I got poked' );
    }

};

var Observer = {

    logEvent: function( data, subject ) {
        console.log( 'Every time: ' + data );
    },

    logEventOnce: function( data, subject ) {
        console.log( 'Just once: ' + data );
        /*
         * THE CRUX
         */
        subject.detach( arguments.callee );
    }
};

Subject.attach( 'testing', Observer.logEvent );
Subject.attach( 'testing', Observer.logEventOnce );

Subject.poke();
//Every time: I got poked
//Just once: I got poked

Subject.poke();
//Every time: I got poked

Subject.poke();
//Every time: I got poked

Subject.poke();
//Every time: I got poked
person meouw    schedule 09.03.2011
comment
Мне это нравится, но вы можете захотеть защититься от слушателей, вызывающих detach из цикла уведомлений субъекта. Длина наблюдателей, изменяемых в цикле for/loop, вызывает проблемы. Много способов решить это. - person selbie; 09.03.2011
comment
Да, это демонстрация, вы хотели бы сделать некоторую поддержку для производства. Хорошее замечание, хотя :) - person meouw; 09.03.2011
comment
Исправлена ​​​​проблема с циклом уведомлений в соответствии с комментарием Селби. Мы не хотим, чтобы массив, который мы перебираем, изменялся во время цикла. - person meouw; 09.03.2011

Если вы передаете ссылку на объект события в своем методе removeEvent, то вы можете просто перебрать все события и протестировать с помощью оператора == — поскольку объект события, который вы передали, является ссылкой, он будет оценен как истина, когда по сравнению с соответствующим объектом события в цикле, поскольку они ссылаются на один и тот же объект в памяти.

person Johnathan Hebert    schedule 09.03.2011
comment
спасибо за ваш ответ Джонатан. Хорошо, чтобы добавить сложности, так как это то, к чему я веду... Если eventObj сложный с элементами, которые являются одновременно объектами и функциями, будет ли == достаточно? Например, event1 = { var type="click"; var callback=function(){alert('Im event1!')}; }; event2 = { var type="click"; var callback=function(){alert('Im event2!')}; }; Сможет ли == определить разницу? - person clockworked247; 09.03.2011
comment
В этом примере события event1 и event2 не равны (==), даже несмотря на то, что они имеют одни и те же свойства с одинаковыми типами — это потому, что они ссылаются на два разных места в памяти. Если вы сделали event2 = event1, то JavaScript просто заставляет event2 хранить ссылку на один и тот же объект в памяти, он не создает копию event1, и, таким образом, они будут равны (==) - person Johnathan Hebert; 09.03.2011