Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@domenic
Last active March 11, 2022 15:25
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save domenic/9003334 to your computer and use it in GitHub Desktop.
Save domenic/9003334 to your computer and use it in GitHub Desktop.
Revealing constructor pattern event-emitter
// This event emitter emits events, but reserves the right to publish events to
// for its creator. It uses a WeakMap for true encapsulation.
const eesToEventMaps = new WeakMap();
export default class EventEmitter {
constructor(publisher) {
const eventMap = Object.create(null);
eesToEventMaps.set(this, eventMap);
publisher(makePublish(this));
}
on(eventName, handler) {
const eventMap = eesToEventMaps.get(this);
let handlers = eventMap[eventName];
if (!handlers) {
handlers = eventMap[eventName] = [];
}
handlers.push(handler);
}
off(eventName, handler) {
const eventMap = eesToEventMaps.get(this);
const handlers = eventMap[eventName];
if (!handlers) {
return;
}
const index = handlers.indexOf(handler);
if (index === -1) {
return;
}
handlers.splice(index, -1);
}
}
function makePublish(ee) {
const eventMap = eesToEventMaps.get(ee);
return function (eventName, ...args) {
const handlers = eventMap[eventName];
if (handlers) {
handlers.forEach(h => h(...args));
}
};
}
const myEE = new EventEmitter(publish => {
// Wait for interesting things to happen, then call
// `publish("eventName", ...args)`.
});
passToOtherCode(myEE);
// The other code only gets access to `on` and `off`: it cannot trigger spurious
// events, but only listen for them. This makes it safe to pass `myEE` to
// multiple consumers without worrying about them accidentally stepping on each
// other's toes.
// (Of course, they could intentionally step on each other's toes, e.g. by
// overwriting `on` or `off`. For true security you'll need to deeply-freeze
// `myEE`, and do a few more things, e.g. to protect against plan interference
// attacks. We're not focused on that right now, but instead on the API
// ergonomics.)
@trusktr
Copy link

trusktr commented Aug 21, 2016

This pattern is awesome!

@trusktr
Copy link

trusktr commented Aug 29, 2017

@domenic, please let me know if I'm close: this pattern of the constructor accepting a publish makes this EventEmitter class not ideal for extending, and more ideal for other code to contain references to an EventEmitter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment