Object#extra hazard

# Andrea Giammarchi (12 years ago)

Just a humble attempt to propose some addiction to the Object.prototype I already know many will kill me for even trying to ...

tl;dr - everything proposed can be already tested through this utility called eddy.js

Event Target All The Things

One of the most widely adopted API in JavaScript world is based on a combination of these methods:

  • .on(type, handler[, capture]) used as equivalent of addEventListener in the DOM namespace, also used in every library that would like to be event driven. Smart enough to avoid duplicated entries for the same handler over the same event type.
  • .once(type, handler[, capture]) used to simplify .on() within an .off() at the top of the invocation to ensure a "one shot only" event
  • .off(type, handler[, capture]) to remove a previously set event handler or silently failif not present
  • .emit(type[, arg1][, argN]) directly from node.js world where almost every object is an EventTarget and EventEmitter anyhow since it's needed and has been proven it's a highly appreciated/welcome approach.
  • .trigger(type[, data]) similar to .emit() except it triggers/fires an event object with some extra method such evt.stopImmediatePropagation() or others DOM related when the event comes from DOM

The proposed implementation lazily assign internal listeners event handlers only once these methods are invoked for the very first time.

This makes every object able to be promoted at runtime as event emitter:

var o = {};
// o is just an object

o.on('event-name', console.log.bind(console));
// now o has internals able to work with handlers

o.trigger('event-name');
// will log an Event object

An Extra "Should Have" Utility

We talk a lot about GC these days and performance impact this has on slower device (or embedded ARM boards such R-PI or Cubieboard, hardware used in production and as server too in some crazy case!).

There is a common anti pattern in JS code since some library introduced Function#bind() and ES decided to adopt it: it creates N new objects per each call.

This makes it impossible to remove listeners so we are forced to address every single object. As example, back to a similar previous code example:

window.addEventListener('load', console.log.bind(console));
// now how to drop that ?

There is no way to remove that listener after that and developers keep passing functions, instead of objects with handleEvent() methods, and more complex code becomes easily messed up.

Object.prototype.boundTo(method) would be able to solve this in a very simple way: creates, if not created already, a bound version of that method so that any further call to the same method will result in the same bound object.

window.addEventListener('load', console.boundTo(console.log));

// whenever we want to ...
window.removeEventListener('load', console.boundTo(console.log));

In my eddy.js proposal this method has a shortcut too, shortcut that might break if some obtrusive minifier as Closure Compiler with ADVANCED option is taking care of the build process. This is why I am not expecting much consensus about the overload of this boundTo() method but here what I've done:

window.addEventListener('load', console.boundTo('log'));

// whenever we want to ...
window.removeEventListener('load', console.boundTo('log'));
// same as
window.removeEventListener('load', console.boundTo(console.log));

The method can accept either a function or a string, where in latter case, the context[methodName] is used to check if the bound object has been created already.

.boundTo(method) should not accept extra arguments since in that case advantages are less than a generic bind due very exponential nature of the possible check over all arguments. In that case developers can use bind and address it if really needed but I've seen this is not the majority of the cases.

Thank You for listening, reading, sharing thoughts, or proposing any change ... I know I'm going to be probably the only one in this planet that will pollute in a non enumerable way the Object.prototype with the stuff I need the most in every single piece of JS software I've written but hey ... maybe it was worth it to try to propose here too :-)

Best

# David Bruant (12 years ago)

I believe events should be part of the object MOP interface (regardless of the [[Prototype]] value). Events are already part of the interface of objects as people use them:

  • in Node.js, events are documented at the same level than properties and methods.
  • In new FirefoxOS WebAPIs, pretty much every new object inherits from EventTarget.

Also, Object properties now have their events (Object.observe), following DOM mutation-related events (DOM Observers API). It won't take long before someone asks for mutation events in ES6 Maps and Sets...

Anyway, I agree with the intent, but I would put the tool at a lower-level if given the choice.

# Jeremy Martin (12 years ago)

Events are already part of the interface of objects as people use them:

  • in Node.js, events are documented at the same level than properties and methods.
  • In new FirefoxOS WebAPIs, pretty much every new object inherits from EventTarget.

I don't think that Node.js is a relevant example here. Events are only exposed on instances (or subclasses) of EventEmitter. This happens to include a lot of the core objects, but it is nonetheless implemented via the [[Prototype]] and object-level properties (rather than anything resembling a MOP). I'm less familiar with FirefoxOS WebAPIs, but the same appears to be true here as well, unless I'm missing something.

# Matthew Robb (12 years ago)

I'd rather do something like:

Object.addEventListener(obj, "eventName", function(){});

I think this matches very closely the way you do property descriptors and I'd love to see

var obj = {
  on eventName() {}
}
# David Bruant (12 years ago)

Le 10/07/2013 19:57, Jeremy Martin a écrit :

I don't think that Node.js is a relevant example here. Events are only exposed on instances (or subclasses) of EventEmitter. This happens to include a lot of the core objects, but it is nonetheless implemented via the [[Prototype]] and object-level properties (rather than anything resembling a MOP). I'm less familiar with FirefoxOS WebAPIs, but the same appears to be true here as well, unless I'm missing something.

That's not my point. I was explaining that people consider events as part of the object interface (regardless of how the event system is implemented).

Currently, to express events in JavaScript, one has to go through a specific [[Prototype]] or extending with a mixin which feels awkward to me. Do we need a mixin to add properties to an object? or a specific [[Prototype]] to add methods?

In the DOM, having to inherit from EventTarget creates this weird situation where events can't be part of WebIDL. It is even rarely possible to be able to list all events supported by a given object (which in turns makes documenting event quite of a nightmare). Supported events have to be aliased via on* properties to be feature-detectable. This madness really has to stop. We need first class events (and I'm praying for a way to retrofit them into the DOM if possible)

# David Bruant (12 years ago)

Le 10/07/2013 20:00, Matthew Robb a ?crit :

I'd rather do something like:

Object.addEventListener(obj, "eventName", function(){});

I think this matches very closely the way you do property descriptors and I'd love to see

var obj = {
  on eventName() {}
}

That's the spirit. I don't care for particular syntax, but that's what I had in mind too. A while ago, I started a library to have first-class events as object properties in specialized proxies 1. It has some inherent limitations but is a start in one direction.

# Anne van Kesteren (12 years ago)

On Wed, Jul 10, 2013 at 2:19 PM, David Bruant <bruant.d at gmail.com> wrote:

This madness really has to stop. We need first class events (and I'm praying for a way to retrofit them into the DOM if possible)

If you don't do the latter, I wonder how you'll achieve the former.

# Matthew Robb (12 years ago)

On further thought I think doing something like:

Object.defineEventListener(obj, "eventName", {
  handler: function(){},
  phase: "before" // , after,  defaults to `on`
});

AND

var obj = {
  on eventName(){},
  after eventName(){},
  before eventName(){}
}
# David Bruant (12 years ago)

Le 10/07/2013 20:27, Anne van Kesteren a écrit :

On Wed, Jul 10, 2013 at 2:19 PM, David Bruant <bruant.d at gmail.com> wrote:

This madness really has to stop. We need first class events (and I'm praying for a way to retrofit them into the DOM if possible) If you don't do the latter, I wonder how you'll achieve the former.

The former can be achieved independently of anything else. It only requires for the ECMA 262 spec to say "now, objects have events!" (like, say, the way Jeremy Martin proposed). Whether that event system works well and intelligently with the current event systems (DOM especially since it has lots of quirks) is a different matter.

But I agree it would be wise to do both at the same time.

# Anne van Kesteren (12 years ago)

Yeah, so what I meant was how you'd stop the madness if you don't do them in a way that works with DOM (as DOM works today). My bad.

# Jeremy Martin (12 years ago)

(like, say, the way Jeremy Martin proposed)

FTR, I haven't proposed anything just yet :)

Although if I did, I think I would be more fond of something like:

Object.mixin({ foo: 'bar' }, EventEmitter);

... that is, I can see the value of events as an object-level API that doesn't imply actual inheritance from something else. The use of mixin is probably a distraction here, but the main point is that it's opt-in. Events inherently have semantics around them, and they're certainly not universal semantics for objects. In fact, event semantics aren't even universal for events (e.g., bubbling). It seems better to me to have some sort of explicit contract before those semantics kick in.

# Andrea Giammarchi (12 years ago)

my implementation works already with DOM nodes and browser environment, including window.

I've also already written an EventTarget mixin object but in my case I'd use that via Object.mixin(Object.prototype, EventTarget); 'cause almost every object I use in my logic should be observed or should emit something to some other object.

Of course Dictionaries are out of the game but that's OK, as long as it's possible to promote them later on via Object.setPrototypeOf(dict, Object.proottype)

I find the Object.addEventListener(obj, "type", handler) approach very boring for the simple reason that it does not make sense to use chainability there, another de-facto thing when it comes to events.

// current eddy.js status
var obj = {}
  .on(type1, handler1)
  .on(type2, handler2)
  .on(type3, handler3)
;

This allows multiple operations with the same object, something not possible with the Object.publicStatic alternative if not repeating same thing many times.

The chainability won here in both jQuery and node.js world, together with many other libraries.

I know it's hard to even think about polluting the Object.prototype but indeed FirefoxOS uses events all over the place, node.js is basically 90% event driven, so is the DOM, and every observable object is firing indirectly events for third parts.

What else do we need to understand that maybe we could simplify this in core and make event driven development way easier and finally more standard?

Even keeping the double firing approach, both trigger and emit since these two do different things (slightly, but still...)

Last wonder, nobody said a word about the .boundTo(method) solution for a very very common/frequent problem.

Thanks for all other thoughts though, best .

# Matthew Robb (12 years ago)

You can do chainability at object define time with the sugar form:

var obj = {
  on type1(){},
  on type2(){},
  on type3(){}
}

OR add to it using one of the following options:

Object.mixin(obj, {
  on type1(){},
  on type2(){},
  on type3(){}
})

obj.{
  on type1(){},
  on type2(){},
  on type3(){}
}

obj := {
  on type1(){},
  on type2(){},
  on type3(){}
}
# Andrea Giammarchi (12 years ago)

I know but people use prefixes and unique ids for events too and I am not sure if that would work so well.

Also, this is not backward compatible, or better, this is impossible to polyfill so it won't bring benefits to the current scenario.

If only I could already Object.mixin(Object.mixin({}, EventEmitter), EventTarget) and alias those prolix names as on() and off() it would be already great but this is too much in between the DOM, behind WebIDL, and ECMAScript ... something I know already historically incompatible, that's why I've proposed new names hopefully one day (and hopefully soon) in core, something that will be inherited in current implementation in any case, as it was in my case simply polluting the Object.prototype with those non enumerable properties luckily inherited in hosted DOM objects too.

best

# Rick Waldron (12 years ago)

I don't want any of this on my Object objects. Not all objects represent something that can or should emit/publish/trigger/broadcast.

For example...

class Light {
  constructor() {
    // initialize Light control instance
  }
  on() {
    // send bits to turn Light on
  }
  off() {
    // send bits to turn Light off
  }
}

So what happens now? All of my Led instances have emit, trigger, once and boundTo methods that are actually useless now. No thanks. I also don't want Object.prototype.* and Object.* to suffer the same way that the [[Global]] (window) object in browsers has suffered. Give a hoot, don't pollute.

Post ES6 world makes creating emitter objects trivial—so why not a blessed standard lib module that can be imported and used to extend a class declaration?

import { Emitter } from "@event";

class Foo extends Emitter {}

Easy.

More inline...

On Wed, Jul 10, 2013 at 12:46 PM, Matthew Robb <matthewwrobb at gmail.com>wrote:

You can do chainability at object define time with the sugar form:

var obj = {
  on type1(){},
  on type2(){},
  on type3(){}
}

This is the dark days of DOM element properties as event handlers:

elem.onclick...

Assigning to this paves over the previous assignment, leaving no way to have multiple handlers registered for the same event type.

On Wed, Jul 10, 2013 at 12:41 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

my implementation works already with DOM nodes and browser environment, including window.

I've also already written an EventTarget mixin object but in my case I'd use that via Object.mixin(Object.prototype, EventTarget); 'cause almost every object I use in my logic should be observed or should emit something to some other object.

Standard lib approach (a la node) works today and tomorrow.

I find the Object.addEventListener(obj, "type", handler) approach very boring for the simple reason that it does not make sense to use chainability there, another de-facto thing when it comes to events.

It's also (subjectively) hideous.

// current eddy.js status
var obj = {}
  .on(type1, handler1)
  .on(type2, handler2)
  .on(type3, handler3)
;

That's great for your use case, but I would never want this by default.

# K. Gadd (12 years ago)

I think at this point we have a long history of language designs where random stuff has been tacked onto the core base class (i.e. Object) that really shouldn't have gone there - hashCode/toString in Java/C#, etc - such that any proposal to put something on Object instances deserves extreme skepticism. Static methods on Object aren't nearly so bad.

In this case, the problem being solved can almost certainly be solved through composition or some other design mechanism. Bolting it onto every instance of Object, even if the only universally paid cost is a reserved name on the prototype, is simply not the right solution unless the downsides of other approaches are intolerable. Which they aren't.

In practice a lot of these use cases call for event subscription and broadcasting to occur through a separate coordinator ('event bus') object and not through the object in question, anyway. This addresses a bunch of reference cycle problems that are caused when you subscribe directly to an object. You don't need to look much further than the DOM for examples...

# Andrea Giammarchi (12 years ago)

On Wed, Jul 10, 2013 at 3:57 PM, Rick Waldron <waldron.rick at gmail.com>wrote:

I don't want any of this on my Object objects. Not all objects represent something that can or should emit/publish/trigger/broadcast.

tis works same way you can define toString during class definition ... so what happens, nothing unexpected.

boundTo methods that are actually useless now.

I think you didn't understand what is boundTo ... completely a part from events.

You can bind any method or function, borrowed or not, and only once, I don't see how you can do the same with current proposal in ES6

genericObject.boundTo(genericMethod) returns always the same bound object.

This is actually trivial enough as utility since chainability is not even an option.

Post ES6 world makes creating emitter objects trivial—so why not a blessed standard lib module that can be imported and used to extend a class declaration?

Because in 10 years I haven't seen such standard Emitter so it's about the time to agree on an event based mechanism, IMO, since we all talk about events in every asynchronous world.

Object.observer ain't good for all cases but only for few of them. We all know how powerful are events.

This is the dark days of DOM element properties as event handlers:

elem.onclick...

it's also more semantic than node.addEventListener('load') since you say and write and mean node.on('load');

What's less semantic is .off() if you want, but it works as counter part.

And back to the light, I would rather use switch(onOrOff) {} as method

Assigning to this paves over the previous assignment, leaving no way to have multiple handlers registered for the same event type.

You are right, in that inline version this is correct. I didn't propose that indeed and you can use more than a handler without problems.

Standard lib approach (a la node) works today and tomorrow.

it works in the meaning that in node you give for granted that basically every object is an EventEmitter.

If there was a mechanism to have that simplified today I believe it would have been used.

It's also (subjectively) hideous.

agreed

That's great for your use case, but I would never want this by default.

So are you willing to talk about that standard EventEmitter and put it in ASAP as native constructor every object could inherit from ?

'cause this would be already a lot for me and everyone else out there, a native way to have emitters in core (hopefully able to work with DOM nodes too)

# Andrea Giammarchi (12 years ago)
false.hasOwnProperty('whatever');
1.0.isPrototypeOf({whate:'ver'});
'string'.propertyIsEnumerable('whatever')

these are already examples of an over-polluted prototype with methods nobody uses ... I thought 4 extra methods everyone is using same way except for Rick that honorees Tesla in this day would not hurt much but once again, my point is that it's really about the time to put some standard mechanism to deal with events in ES code.

The fact Object.observe is already accepting a handler with an argument which is fired as an event smells to me ... I'd rather define upfront a common definition of handlers then propose any extra method based on them ;-)

# Jeremy Martin (12 years ago)

Once again, this problem goes beyond simply polluting the prototype (which is enough of an issue by itself). The semantics for Emitters are somewhat of an 80/20 thing: 80% of all emitters need some common behavior, but the remaining 20% is custom.

Going back to Node.js, for instance: everything would be great with emitter.on(...), emitter.once(...), emitter.off(...), emitter.emit(...), etc.

But now we need emitter.listeners(event), emitter.setMaxListeners(n), etc. So, we still end up with a custom EventEmitter that implements the additional node-specific stuff, but we have a new problem: how do we implement emitter.listeners(event)? Without an exposed emitter._listeners (or similar) mapping, we're hosed. So do we monkey-patch the listener (de)registration methods? Yada yada, you get the point.

The same problem exists with DOM events and most likely in any other non-trivial Emitter scenario. As I see it, it simply isn't practical at this level. The pollution issue goes far beyond 4 or 5 or even 10 new properties/methods if it's going to be robust enough to handle the already-common use cases, and if anything is missed developers will still be forced to sub-class it anyway (which is the only hassle that might've been worth avoiding in the first place).

Alternatively, as Rick said, we could simply have:

class Foo extends Emitter {}
// or
class Foo extends EventEmitter {}
// or
class Foo extends EddyEmitter {}
// etc.

This, IMO, is far more favorable and far less obtrusive (and already in the works).

# Andrea Giammarchi (12 years ago)

node.js allows same listener type with same handler multiple times ... but it has maxListeners number which is kinda pointless if events are not even tracked and can be duplicated.

node also has this thing that you can drop everything from an emitter even if you don't own registered events which is, in my opinion, undesired, specially when you pass around your own modules expecting them to react in certain cases.

That said, node.js folks had to quickly define/decide what was needed and that's what they came up with ... it worked until now but I think I've never seen a module that really needed all that API (there are too many out there though so I might be wrong)

Not having this in core means every implementation will differ somehow and this is the annoying part ... there's no way to write code that is compatible with both worlds or all libraries without "sniffing" the surrounding environment.

Let's say my module would like to intercept some generic event and react ... no standard way to even know which was the fired event in node, since no type is passed, no easy way to check for errors in DOM world.

Keep telling devs to create their own Emitter because of this and that weird case that should probably not exist in first place, and here I believe that DOM and DOM based libraries have more experience than node.js, cannot benefit anyone in both worlds for something needed since the very beginning in every organized asynchronous situation.

As example, I do like the {handleEvent:function(evt){}} concept and used that a lot without needing to bind anything because the context is always the expected one (unless handleEvent has been bound differently, of course) ... in node I need to be careful that no extra listeners are added but the amount of garbage created per each Emitter usage and bound objects is not considered a problem? This is just weird, as weird is the decision that since it's hard to make it right we rather prefer anarchy around the topic per each slightly different, who knows why, behavior ...

node.js could have simply followed DOM logic passing Event or instanceof Error as single argument, there is massive fragmentation there already but here there was my attempt to bring more harmony around events.

node.js guys are OK with that, I can cope with that too, but having 2 different models without any win in both environment does not look like the right approach to me.

Anyway, I started saying I was not even expecting people considering a change ... so it's OK.

Best

# Rick Waldron (12 years ago)

On Wed, Jul 10, 2013 at 7:22 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

tis works same way you can define toString during class definition ... so what happens, nothing unexpected.

This response completely dodges the point I've made and toString is a poor comparison. toString is only one method and it's expected that a class (or previously Constructor.prototype.toString) would define it's own toString with an appropriate return value.

I think you didn't understand what is boundTo ... completely a part from events.

No, I understood it just fine. I was just listing out every method listed here WebReflection/eddy#event-driven-js but you snipped the rest of the sentence, so now it's out of context.

Because in 10 years I haven't seen such standard Emitter so it's about the time to agree on an event based mechanism, IMO, since we all talk about events in every asynchronous world.

What I'm saying is: let's agree to an Emitter and make it a standard library module.

it's also more semantic than node.addEventListener('load') since you say and write and mean node.on('load');

What are you talking about? Look again at the example I'm commenting on and explain to me how to register two different handlers for an event named "type1". Yes, addEventListener sucks. Yes, on("foo", handler) is great.

You are right, in that inline version this is correct. I didn't propose that indeed and you can use more than a handler without problems.

Of course, I was referring to the syntactic form that Matthew proposed:

var obj = {
  on foo() {}
}

(this is still part of my point above, re: registering multiple handlers for the same named event)

If there was a mechanism to have that simplified today I believe it would have been used.

It may not be "simple" by your definition (it isn't simple by mine), but it's the most common practice...

var Emitter = require("events").EventEmitter;

function Foo() {
  Emitter.call(this);
}

Foo.prototype = Object.create(Emitter.prototype);
Foo.prototype.constructor = Foo;

Which corresponds to:

class Foo extends Emitter {
  constructor() {
    super()
  }
}

Maybe I'm missing something in your argument here? If so, I apologize.

So are you willing to talk about that standard EventEmitter and put it in ASAP as native constructor every object could inherit from ?

I absolutely stand by championing such a standard module.

'cause this would be already a lot for me and everyone else out there, a native way to have emitters in core (hopefully able to work with DOM nodes too)

Well, any object right?

# Rick Waldron (12 years ago)

On Wed, Jul 10, 2013 at 7:27 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

false.hasOwnProperty('whatever');
1.0.isPrototypeOf({whate:'ver'});
'string'.propertyIsEnumerable('whatever')

these are already examples of an over-polluted prototype with methods nobody uses ... I thought 4 extra methods everyone is using same way except for Rick that honorees Tesla in this day would not hurt much but once again, my point is that it's really about the time to put some standard mechanism to deal with events in ES code.

The fact Object.observe is already accepting a handler with an argument which is fired as an event smells to me ... I'd rather define upfront a common definition of handlers then propose any extra method based on them ;-)

But they're different things entirely. Object.observe is an implicit invocation, observer pattern (some may accuse me of being redundant there, I forgive them) that announces the relevant states and state changes in the life of an object.

# Andrea Giammarchi (12 years ago)

On Wed, Jul 10, 2013 at 6:16 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

I absolutely stand by championing such a standard module.

then I stop here waiting for other opinions about championing an Emitter mixin/module/constructor in core.

# Rick Waldron (12 years ago)

On Wed, Jul 10, 2013 at 9:31 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

then I stop here waiting for other opinions about championing an Emitter mixin/module/constructor in core.

I feel like we've made progress tc39/agendas#6

# Brian Di Palma (12 years ago)

I'm in favour of this standard class approach.

I just hope that when discussing this people would also entertain a non-string event type.

emitter.addListener( MY_EVENTS.PERMISSION_UPDATE, callback );

So in a modern IDE you could click on MY_EVENTS.PERMISSION_UPDATE and it would display the object which could contain the arguments the callback is provided with and a place to document the event. It might also improve minifying/refactoring and searching for occurrences of the event much easier then if it where a simple string.

# Andrea Giammarchi (12 years ago)

I would simplify saying that symbols can be used as well as strings ? I don't see any usefulness into using an object as event type and I think if we start olready over-engineered/complicated this will never see the light in any spec which is a lost/lost if you ask me

# Rick Waldron (12 years ago)

On Thu, Jul 11, 2013 at 4:45 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

I would simplify saying that symbols can be used as well as strings ? I don't see any usefulness into using an object as event type and I think if we start olready over-engineered/complicated this will never see the light in any spec which is a lost/lost if you ask me

It's trivial if an Emitter uses something like [[MapData]] or [[WeakMapData]] as it's internal data property for event storage.

# Andrea Giammarchi (12 years ago)

trivial like ... 2 weak maps + a set for a single event ?

obj.on(evt, handler);

.. internals ...

// wm as private internal generic WeakMap
if (!wm.has(obj)) {
  // creates related WeakMap
  wm.set(obj, new WeakMap);
}
if (!wm.get(obj).has(evt)) {
  wm.get(obj).set(evt, new Set);
}
wm.get(obj).get(evt).add(handler);

I have the feeling it's very true in JS world nobody ever thinks about RAM and GC ^_^

That said, it becomes over complicated for non concrete reason/use case if we have strings since AFAIK WeakMap does not accept strings as key.

For all this time the event type as string has blocked like ... nobody ever ? I wonder again what's the benefit then to instantly over complicate an API at this stage instead of looking around what worked already for everyone.

Maybe it's me not seeing the power of objects as events.

# Matthew Robb (12 years ago)

With the availability of constants and Symbols you could easily create what SHOULD be a memory efficient event library.

# Brian Di Palma (12 years ago)

I didn't suggest that strings as event type would block anyone, strings work fine.

I asked for some consideration to be given to also being able to use a unique object as the type key. I gave some reasons, copied below.

So in a modern IDE you could click on MY_EVENTS.PERMISSION_UPDATE and it would display the object which could contain the arguments the callback is provided with and a place to document the event. It might also improve minifying/refactoring and searching for occurrences of the event much easier then if it where a simple string.

None of which are critical but could be handy when dealing with larger code bases and your event is simply called "click".

I'm not too wedded to the idea, but I can imagine that if you are building applications with hundreds of packages and modules it might be a safer/easier way to identify instances of your specific event listeners.