1

I wish to create a class which extends EventTarget, allowing addEventListener() and dispatchEvent() to be invoked on it.

Logically, my class wants to be a singleton; if a page accidentally instantiated more than one, its functionality would break. For this reason, I've implemented the entirety of this class's logic as static fields and methods.

Unfortunately extends doesn’t seem to mesh neatly with static, so this doesn’t work:

class MyClass extends EventTarget {
   static dispatch(type) { MyClass.dispatchEvent(new Event(type)); }
}
MyClass.dispatch('hello');

Uncaught TypeError: MyClass.dispatchEvent is not a function

I also tried:

class MyClass extends EventTarget {
   static dispatch(type) { MyClass.prototype.dispatchEvent(new Event(type)); }
}
MyClass.dispatch('hello');

Uncaught TypeError: 'dispatchEvent' called on an object that does not implement interface EventTarget.

There must be something I can do with constructors and prototypes to bring EventTarget's methods directly into MyClass. Could someone enlighten me as to how I need to think through this? (Aside from the awkward hack of declaring a class static variable containing an EventTarget, and forwarding things to/from this.)

7
  • Have you tried using EventTarget.dispatchEvent() instead? You're not overriding the method, so there's no need to refer to your own class.
    – Barmar
    Commented Apr 22 at 18:05
  • But I'd expect that dispatchEvent() needs to know this, which isn't available in a static method.
    – Barmar
    Commented Apr 22 at 18:06
  • 1
    this in an EventTarget is supposed to be the DOM element that the event is dispatched on. It shouldn't be the class itself.
    – Barmar
    Commented Apr 22 at 18:16
  • 1
    @Barmar I must respectfully disagree here: The whole point of making EventTarget inheritable is to make it available to objects which are not DOM elements: link
    – John Leary
    Commented Apr 22 at 19:00
  • 1
    If you're creating a singleton, then it should be that single object, not the class itself.
    – Barmar
    Commented Apr 22 at 19:04

2 Answers 2

2

Logically, my class wants to be a singleton

In that case, forget about using a "static" class altogether. Simply create an object!

const MyClass = new EventTarget();

Then add your methods to that object:

MyClass.dispatch = function(type) {
  MyClass.dispatchEvent(new Event(type));
};

and you can invoke it as

MyClass.dispatch('hello');
0
1

Couple of issues here:

  1. You are invoking dispatchEvent as if it is a static method, but it is not. You need to invoke it for an instance. And that means using this

enter image description here

  1. The way you are trying to implement singleton is a little different. Making methods static will not ensure that no one can create multiple instances.

You can add that logic in the constructor:

class MyClass extends EventTarget {
  static #instance;

  constructor() {
    if (MyClass.#instance) {
     //Use either this           
     //throw Error("Only one instance of MyClass can be created"); 
      return MyClass.#instance; //or this
    }
    super();
    MyClass.#instance = this;
  }

  dispatch(type) {
    console.log("dispatching event",type);
    this.dispatchEvent(new Event(type));
  }
}

// Usage
const singletonInstance = new MyClass();

singletonInstance.dispatch('event 1');

singletonInstance.dispatch('event 2');

const doubletonInstance = new MyClass(); // === singletonInstance

Tip: If you are using modules, then there is no need for this. Just never export your class and only export the object.

Link

8
  • Makes sense. Done. I think showing the modules will not be possible right? So I did not do for that one. Commented Apr 22 at 18:18
  • I think I see the core problem — by avoiding instantiation, I prevent super() from ever being called, so no EventTarget internal state is created for its methods to work with. One way or another, super() needs to be called. I don't need to punish callers for trying to instantiate a second copy of the class, so I'd change the MyClass constructor to return MyClass._instance (if already set) instead of throwing an error. It's too bad _instance is public as a side effect of this!
    – John Leary
    Commented Apr 22 at 18:54
  • I changed _instance to #instance to make it private.
    – John Leary
    Commented Apr 22 at 19:44
  • Yes, correct. That you can do. But I do look at the link I have shared. In case you are using modules, there is not even a need to do all this as you only export the object, Commented 2 days ago
  • "If you are using modules, just never export your class and only export the object" - that won't make it a singleton, the class could still be instantiated multiple times. When using modules, better use multiple named exports instead of exporting an object.
    – Bergi
    Commented yesterday

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.