Event oriented programming promotes low coupling but not necessarily high cohesion. Always consider the Single Responsibility Principle while designing collaboration through events. I see three primary ways of collaboration:

One to One

A single object communicates with other (a collaborator). This can be done with dependency injection the same way we do it in Java or any other language (and you don’t need Spring for that!). JavaScript provides another mechanism for one2one communication, the DOM Level 0 traditional way of event handling. A very useful and powerful way of adding hooks/ports to plug new behavior in, still promoting low coupling. If you need to change the collaboration in the future, from one2one towards one2many, it can be done easily. In some cases I see this as a more decoupled way of dependency injection.

function Car(){
   this.go = function(){
        /* some calculation over here */
        this.onLowFuel();
   };
   this.onLowFuel = function(){/* event */}
}
function RacingTeam(){
   function getReadyToFuel = function(){/* whatever */};

   this.addCarToRace = function(car){
      car.onLowFuel = function(){
          console.log('low fuel!', this);
          getReadyToFuel();
      }
   };
}
var car = new Car();
var team = new RacingTeam();
team.addCarToRace(car);

As you might realize I know nothing about car races 🙂 but I hope you understand the code. When implementing traditional dependency injection, the car knows it depends on a team, but it doesn’t know the particular implementation of the team. With the DOM level-0 traditional way, the car doesn’t even know it will be used in a team. All it knows is that it has to inform someone else about its fuel level when it gets low. However, the team’s got to know about the car. Still the team should not know about the particular implementation of the car.
To test-drive this behavior, you probably need two steps. First one, make sure the car triggers the event and second, make sure the team handles the event. I wrote the two tests in a previous post. DOM level-0 traditional is the main mechanism I use for one to one collaboration, it’s got however some pitfalls worth considering.

Pitfalls
  • You might not know some object is already handling the event: the handling mechanism is simple, we are just replacing the original function in the object triggering the event. If we try handle the same event twice, there will be only one handler, the last one. I always use the “on” prefix for those methods that trigger events as a convention to know it is an event with a single handler.
  • You might make mistakes with the “this” keyword: in the code above, what do you think “this” is when we invoke console.log? It’s the car object, not the team. It’s a problem with a very easy solution though. I usually have a reference to the handler doing “var self = this;”. Some people prefer the “bind” method to do the same.

One to Many

For one to many collaboration there is the Observer pattern. jQuery implements the observer pattern to expose events such as “click”:

$('#closeButton').click(function(){
    /* the event handler 1 */
    console.log('handler1');
});
$('#closeButton').click(function(){
    /* the event handler 2 */
    console.log('handler2);
});

When the button is clicked, both handlers are executed. When I implement the observer pattern in my objects, I prefer to make it explictly:

objectTriggeringTheEvents.addSubscriber('eventName', handler1);
objectTriggeringTheEvents.addSubscriber('eventName', handler2);

The “addSubscriber” makes clear to me, that I can have several subscribers. This is specially important to distinguish from one2one collaboration.
As an exercise I leave out to you the implementation of the observer pattern with TDD.
I use one2many collaboration when I really need the object to be observed by several objects, which happens to me, less often than one2one collaboration.

Many to Many

Different objects might trigger the same event and there might be a bunch of other objects subscribing that event. This is the case of a many2many collaboration. Or, I might not be interested in the source of the event, I might just want to handle it. This usually happens at the infrastructure level. For example I use it to know when the window is being closed. I don’t care who is responsible for realizing the window is being closed but I do want to know it in several parts of my application to shutdown properly. So I create an object which subscribes to “window.onbeforeunload” to be aware of window closing and in turn, emits the event through a “bus” where subscribers are listening.
This is the less coupled way of interaction between objects, but it comes with counterparts: code gets harder to follow. This is the publish/subscribe pattern. My advise is to use this pattern only when strictly necessary and not as a the default mechanism for event oriented programming.
There are several open source libraries implementing this pattern. As an exercise, try to develop it from scratch, test-first. This is the code I ended up with while implementing my own pub/sub:

EventBus = function(){
  var subscribersInfo = [];

  this.addSubscriber = function(callback){
      var eventNames = [].slice.call(arguments).slice(1);
      subscribersInfo.push({
        subscriber: callback, eventNames: eventNames});
  };

  this.emit = function(eventName, eventArgs){
      for(var i = 0, len = subscribersInfo.length; i < len; i++){
          var info = subscribersInfo[i];
          for (var j = 0, lenj = info.eventNames.length; j < lenj; j++){
              if (info.eventNames[j] == eventName)
                  info.subscriber(eventName, eventArgs);
          }
      };
  }
};