Tha*_*you 25 javascript events
我想在我的客户端程序中创建一个自定义事件发射器.我正在引用EventTarget的这个(稀疏)文档
我的实施尝试
var Emitter = function Emitter() {
EventTarget.call(this);
};
Emitter.prototype = Object.create(EventTarget.prototype, {
constructor: {
value: Emitter
}
});
Run Code Online (Sandbox Code Playgroud)
我想要的用法
var e = new Emitter();
e.addEventListener("hello", function() {
console.log("hello there!");
});
e.dispatchEvent(new Event("hello"));
// "hello there!"
Run Code Online (Sandbox Code Playgroud)
哪里失败了
var e = new Emitter();
// TypeError: Illegal constructor
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
更新
以下是可能的,但它是一个依赖于虚拟DOMElement的hack
var fake = document.createElement("phony");
fake.addEventListener("hello", function() { console.log("hello there!"); });
fake.dispatchEvent(new Event("hello"));
// "hello there!"
Run Code Online (Sandbox Code Playgroud)
我想知道如何在不使用虚拟元素的情况下完成此操作
Tha*_*you 33
我不久前放弃了这一点,但最近再次需要它.这就是我最终使用的内容.
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'))Run Code Online (Sandbox Code Playgroud)
ES5
function Emitter() {
var eventTarget = document.createDocumentFragment()
function delegate(method) {
this[method] = eventTarget[method].bind(eventTarget)
}
[
"addEventListener",
"dispatchEvent",
"removeEventListener"
].forEach(delegate, this)
}
// sample class to use it
function Example() {
Emitter.call(this)
}
// run it
var e = new Example()
e.addEventListener("something", function(event) {
console.log(event)
})
e.dispatchEvent(new Event("something"))Run Code Online (Sandbox Code Playgroud)
是啊!
对于那些需要支持旧版本的ecmascript的人,请转到此处
// IE < 9 compatible
function Emitter() {
var eventTarget = document.createDocumentFragment();
function addEventListener(type, listener, useCapture, wantsUntrusted) {
return eventTarget.addEventListener(type, listener, useCapture, wantsUntrusted);
}
function dispatchEvent(event) {
return eventTarget.dispatchEvent(event);
}
function removeEventListener(type, listener, useCapture) {
return eventTarget.removeEventListener(type, listener, useCapture);
}
this.addEventListener = addEventListener;
this.dispatchEvent = dispatchEvent;
this.removeEventListener = removeEventListener;
}
Run Code Online (Sandbox Code Playgroud)
用法保持不变
Bergi对于该部分是正确的,这EventTarget只是一个接口而不是构造函数.
js中有多个对象是有效的事件目标.如前所述有:
元素,文档,窗口是最常见的事件的目标,但也有其他例如Websocket.无论如何,所有这些都给出了.
如果你做一个简短的测试,你会发现一些事情:
EventTarget.isPrototypeOf(WebSocket); // true
var div = document.createElement("div");
EventTarget.isPrototypeOf(div.constructor); // true
typeof EventTarget // function
EventTarget() // TypeError: Illegal constructor
Run Code Online (Sandbox Code Playgroud)
EventTarget是这些构造函数的原型,这是你不能为任何其他构造函数设置的东西(即使你可以,它也可能不起作用).它也是一个功能,但不是可调用的功能.
现在这是你问的时候:那有什么EventTarget好处,我该如何使用呢?
我们有3个方法,每个事件发射器需要实现,并且可能需要将这些方法绑定在一起,因此我们有一个接口.这意味着您不能EventTarget用于调用目的,但其他一些本机功能可能会.这类似于创建元素,我们有document.createElement工厂方法,我们不(不能)new HTMLDivElement()用来创建新元素,但我们可以比较两个元素的构造函数.
结论
如果要创建自定义事件发射器,则始终必须创建一些虚拟对象或使用已存在的虚拟对象.从我的观点来看,它将是什么对象并不重要.
某些方法不可调用,但仍可以作为对象的属性进行比较.因此它们是可见的.EventTarget是其中之一.
根据浏览器支持的不同,有 3 种方法可以实现此目的。
1) EventTarget 现在是可构造的,所以只需扩展它:
class MyEventTarget extends EventTarget {
constructor(){
super()
}
}
Run Code Online (Sandbox Code Playgroud)
2) DOM 'Node' 接口实现了 EventTarget,所以只需实现它即可:
function MyEventTarget(){
var target = document.createTextNode(null);
this.addEventListener = target.addEventListener.bind(target);
this.removeEventListener = target.removeEventListener.bind(target);
this.dispatchEvent = target.dispatchEvent.bind(target);
}
MyEventTarget.prototype = EventTarget.prototype;
Run Code Online (Sandbox Code Playgroud)
3)自己动手(假设没有选项参数)并调度异步:
function MyEventTarget(){
this.__events = new Map();
}
MyEventTarget.prototype = {
addEventListener(type, listener){
var listeners = this.__events.get(type);
if(!listeners){
listeners = new Set();
this.__events.set(type, listeners);
}
listeners.add(listener);
},
removeEventListener(type, listener){
var listeners = this.__events.get(type);
if(listeners){
listeners.delete(listener);
if(listeners.size === 0){
this.__events.delete(type);
}
}
},
dispatchEvent(event){
var listeners = this.__events.get(event.type);
if(listeners){
for(let listener of listeners){
setTimeout(listener.call(null, event), 0);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果需要,请将 Map()/Set() 替换为 {}/[]。
所有这 3 个选项都可以通过以下方式进行测试:
var target = new MyEventTarget();
target.addEventListener('test', (e) => {console.log(e.detail);}, false);
var event = new CustomEvent('test', {detail : 'My Test Event'});
target.dispatchEvent(event);
Run Code Online (Sandbox Code Playgroud)
任何需要实现您自己的“EventTarget”接口的对象都可以像本机对象一样继承它:
function Person(name){
MyEventTarget.call(this);
this.__name = name;
}
Person.prototype = {
__proto__ : MyEventTarget.prototype,
get name(){ return this.__name;}
}
Run Code Online (Sandbox Code Playgroud)
EventTarget现在已在DOM生活标准中指定为可构造。它在支持Chrome浏览器64(已出)和火狐59(即将3月13日)。
在不考虑浏览器支持的情况下,EventTarget无法将其实例化为构造函数,而只是通过另一个功能示例来丰富此问题。
根据Mozilla自己在该日期(2018 年 10 月 7 日)描述的兼容性列表:
事件目标(构造函数):
延伸:
class Emitter extends EventTarget {
constructor() {
super()
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在许多事件插件中创建通用方法,例如:、 和on()(off()使用):.once()emit()CustomEvent
class Emitter extends EventTarget {
constructor() {
super()
}
}
Run Code Online (Sandbox Code Playgroud)
EventTarget()现在大多数现代浏览器都支持构造函数。
对于仍然不支持它的浏览器,有一个polyfill可用。
这意味着它很简单:
var e = new EventTarget();
e.addEventListener("hello", function() {
console.log("hello there!");
});
e.dispatchEvent(new CustomEvent("hello"));
// "hello there!"
Run Code Online (Sandbox Code Playgroud)
为了完整起见,在 Node 或 Electron 应用程序中你会这样做
var EventEmitter = require('events');
var e = new EventEmitter();
e.addListener("hello", function() {
console.log("hello there!");
});
e.emit("hello")
// "hello there!"
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16165 次 |
| 最近记录: |