Как использовать JavaScript EventTarget?
Я хотел бы создать персонализированный эмитент событий в своих клиентских программах. Я ссылаюсь на эту (разреженную) документацию для EventTarget
Моя попытка реализации
var Emitter = function Emitter() {
EventTarget.call(this);
};
Emitter.prototype = Object.create(EventTarget.prototype, {
constructor: {
value: Emitter
}
});
Желаемое использование
var e = new Emitter();
e.addEventListener("hello", function() {
console.log("hello there!");
});
e.dispatchEvent(new Event("hello"));
// "hello there!"
Где он терпит неудачу
var e = new Emitter();
// TypeError: Illegal constructor
Что я делаю неправильно?
Обновление
Возможно следующее, но это хак, который зависит от фиктивного DOMElement
var fake = document.createElement("phony");
fake.addEventListener("hello", function() { console.log("hello there!"); });
fake.dispatchEvent(new Event("hello"));
// "hello there!"
Я хотел бы знать, как это сделать, не используя фиктивный элемент
Ответы
Ответ 1
Я отказался от этого некоторое время назад, но в последнее время нуждался в нем снова. Вот что я в итоге использовал.
ES6
class Emitter {
constructor() {
var delegate = document.createDocumentFragment();
[
'addEventListener',
'dispatchEvent',
'removeEventListener'
].forEach(f =>
this[f] = (...xs) => delegate[f](...xs)
)
}
}
// sample class to use Emitter
class Example extends Emitter {}
// run it
var e = new Example()
e.addEventListener('something', event => console.log(event))
e.dispatchEvent(new Event('something'))
Ответ 2
Берги был прав относительно части, что EventTarget
- это просто интерфейс, а не конструктор.
В js есть несколько объектов, которые являются действительными целями событий. Как упоминалось там:
Элемент, документ и окно являются наиболее распространенными целями событий, но есть и другие, например Websocket
. В любом случае, все они даются.
Если вы сделаете короткий тест, вы можете заметить несколько вещей:
EventTarget.isPrototypeOf(WebSocket); // true
var div = document.createElement("div");
EventTarget.isPrototypeOf(div.constructor); // true
typeof EventTarget // function
EventTarget() // TypeError: Illegal constructor
EventTarget
является прототипом этих конструкторов, который вы не можете установить для какого-либо другого конструктора (и даже если бы вы могли, он, вероятно, не работал). Также это функция, но не вызываемая.
Теперь настало время, когда вы спрашиваете: И что это за EventTarget
хорошо и как я могу его использовать?
У нас есть 3 метода, которые необходимо реализовать каждому эмиттеру событий, и, вероятно, необходимо связать эти методы вместе, поэтому у нас есть интерфейс для них. Это означает, что вы не можете использовать EventTarget
для целей вызова, но некоторые другие собственные функции могут. Это похоже на создание элементов, у нас есть метод document.createElement
factory, и мы не можем (не можем) использовать new HTMLDivElement()
для создания нового элемента, но мы можем сравнить конструкторы из двух элементов.
Заключение
Если вы хотите создать собственный эмиттер событий, вам всегда нужно создать какой-нибудь фиктивный объект или использовать уже существующий. С моей точки зрения, не имеет значения, какой именно объект будет.
Некоторые методы не подлежат вызову, но их можно сравнить как свойства объектов. Поэтому они видны. EventTarget
является одним из них.
Ответ 3
Вот как это сделать, используя CustomEvent
, кросс-браузер (fiddle):
// listen to event
window.addEventListener("say", function(e) { alert(e.detail.word); });
// create and dispatch the event
var event = document.createEvent("CustomEvent");
event.initCustomEvent('say', true, true,
{ "word": "Hello!" });
window.dispatchEvent(event);
Вам нужно будет использовать window
или document
или любой другой существующий элемент DOM для регистрации listeneres и отправки события. EventTarget
не является объектом, это интерфейс. Попробуйте открыть EventTarget
в консоли JavaScript, и вы увидите это.
Ответ 4
EventTarget
теперь указан как конструктивный в уровне жизни DOM. Он поддерживается в Chrome 64 (уже отключен) и Firefox 59 (ожидается 13 марта).
Ответ 5
Попробуйте простую реализацию ES6.
class DOMEventTarget {
constructor() {
this.listeners = new Map();
}
addEventListener(type, listener) {
this.listeners.set(listener.bind(this), {
type, listener
});
}
removeEventListener(type, listener) {
for(let [key, value] of this.listeners){
if(value.type !== type || listener !== value.listener){
continue;
}
this.listeners.delete(key);
}
}
dispatchEvent(event) {
Object.defineProperty(event, 'target',{value: this});
this['on' + event.type] && this['on' + event.type](event);
for (let [key, value] of this.listeners) {
if (value.type !== event.type) {
continue;
}
key(event);
}
}
}
let eventEmitter = new DOMEventTarget();
eventEmitter.addEventListener('test', e => {
console.log('addEventListener works');
});
eventEmitter.ontest = e => console.log('ontype works');
eventEmitter.dispatchEvent(new Event('test'));
Ответ 6
Пример фрагмента кода для использования javascript EventTarget
// attach event
var ev = EventTarget.prototype.addEventListener.call(null, 'alert', () => alert('ALERTED'))
// dispatch event
ev.dispatchEvent.call(null, new Event('alert'))