Множественные события для объекта
Подскажите, есть ли в js обработчик множественных событий для объектов? Сейчас реализовано так:
obj = {
ev: function() {},
actions: function() {
...
this.ev();
}
}
... //Здесь создается новый объект типа "obj"
newObj.ev = function() {
...
}
В этом случае, если потребуется назначить для ev второй обработчик, то он перезапишет первый. Для DOM есть addEventListener, есть ли что-то подобное для обычных объектов?
Ответы (2 шт):
Я использую довольно простую реализацию Emitter на манер node.js но для браузера. Просто наследуете класс от него.
/**
* The Emitter is a simple emitter class that provides you with `on()`, `off()` and `trigger()` methods
* @class Emitter
*/
export default class Emitter {
callbacks = new Map();
context;
isElement = false;
constructor(context) {
this.context = context;
this.isElement = context instanceof Element;
}
/**
* Registers callbacks by event name
* @param {String} type
* @param {Function} callback
*/
on(type, callback) {
if (this.isElement) {
this.context.addEventListener(type, callback);
}
if (!this.callbacks.has(type)) {
this.callbacks.set(type, new Set());
}
this.callbacks.get(type).add(callback);
return this;
}
/**
* Deregister callbacks by event name
* @param {String} type
* @param {Function} callback
*/
off(type, callback) {
if(!type && !callback){
for(let type of this.callbacks.keys()){
if(this.isElement){
let callbacks = [...this.callbacks.get(type)];
for(let callback of callbacks){
this.context.removeEventListener(type, callback);
}
}
this.callbacks.get(type).clear();
}
}
else if (!this.callbacks.has(type)) {
return null;
}
else if (!callback) {
if (this.isElement) {
const callbacks = [...this.callbacks.get(type)];
for (let callback of callbacks) {
this.context.removeEventListener(type, callback);
}
} else {
this.callbacks.get(type).clear();
}
} else {
if (!this.callbacks.get(type).has(callback)) {
return null;
}
this.callbacks.get(type).delete(callback);
if (this.isElement) {
this.context.removeEventListener(type, callback);
}
}
return this;
}
/**
* Triggers event callbacks by event object
* @param {AbstractEvent} event
*/
trigger(event) {
if (this.isElement) {
this.context.dispatchEvent(event);
} else {
if (!this.callbacks.has(event.type)) {
return null;
}
const callbacks = [...this.callbacks.get(event.type)];
const caughtErrors = [];
for (let i = callbacks.length - 1; i >= 0; i--) {
const callback = callbacks[i];
try {
callback(event);
} catch (error) {
caughtErrors.push(error);
}
}
if (caughtErrors.length) {
/* eslint-disable no-console */
console.error(
`Emitter caught errors while triggering '${event.type}'`,
caughtErrors
);
/* eslint-disable no-console */
}
}
return this;
}
/**
* trigger alias
*/
fire(...args){
this.trigger(...args);
}
/**
* trigger alias
*/
emit(...args){
this.trigger(...args);
}
}
Еще один, более простой, вариант реализации с глобальным менеджером событий. Создается событие с определенным именем, например, "death". В любом месте скрипта можно "подписаться" на это событие. При возникновении события должна быть вызвана функция callEvent, которая обойдет и вызовет все "подписанные" на него функции. Определить, кто вызвал событие, можно по объекту target, который передается в функцию.
Очень простая реализация, но в некоторых ситуациях ее вполне может хватить.
events = {
items: {},
addEvent: function(event) {
this.items[event] = [];
},
addListener: function(event, func) {
this.items[event].push(func);
},
callEvent: function(event, target) {
if (typeof this.items[event] == 'undefined') return false;
this.items[event].forEach(function(value, index) {
value(target);
});
}
}
mainObj1 = {
id: 'main 1',
init: function() {
var self = this;
events.addEvent('death');
setTimeout(function() {
console.log(self.id + ' - инициализировал событие');
events.callEvent('death', self);
}, 2000);
}
}
mainObj2 = {
id: 'main 2',
init: function() {
var self = this;
events.addEvent('death');
setTimeout(function() {
console.log(self.id + ' - инициализировал событие');
events.callEvent('death', self);
}, 5000);
}
}
obj1 = {
id: 'obj1',
init: function() {
var self = this;
events.addListener('death', function(target) {
console.log(self.id + ' - получил событие от ' + target.id);
});
}
}
obj2 = {
id: 'obj2',
init: function() {
var self = this;
events.addListener('death', function(target) {
console.log(self.id + ' - получил событие от ' + target.id);
});
}
}
mainObj1.init();
mainObj2.init();
obj1.init();
obj2.init();