Custom constructors with mixins to add event bindings in JavaScript

A few years ago, Douglas Crockford wrote a seminal post on prototypal inheritance in JavaScript where he argued:

[The new keyword] was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript.

Crockford suggested that a JavaScript programmer could create a constructor function to generate objects using prototypes and bypass the new keyword. As an added benefit, the constructor function could introduce other functionality to the generated object, providing benefits that the new keyword could not provide.

While building JavaScript client apps, I have come across many situations where I would like to execute a method in an arbitrary object, and once that method has executed, I would like to chain other methods to it. JavaScript provides events and event handlers for you to chain methods against DOM events, but what if you don't want to register an event listener against a DOM element? What if the object whose methods you want to bind to is not associated with a particular DOM element?

 

Tiny-pubsub

There are a few solutions to this problem. Ben Alman wrote a library called tiny-pubsub that uses jQuery to broadcast events that can be subscribed to. Using tiny-pubsub, you can subscribe to events like this:

$.subscribe( "user.login", function( evt, data ){
    //args passed {[username], [password], [runApp ]}
    app.remote.user.login( data );
});

And then generate the events like so:

var data = {
     username : $( "input[name='username']" ).val( ),
     password : $( "input[name='password']" ).val( )
};

$.publish( "user.login", data );

 

Traditional Event objects

Without using jQuery, you can always register an event listener against the document object and trigger an event against it:
 

document.addEventListener("click", function(){
    login( user, pass);
});


Then you create an Event object and dispatch it:
 

var event = new Event('login', { user: username, pass: password } );

// Dispatch the event.
document.dispatchEvent(event);

 

These techniques work well for functions like login, but I have encountered some limitations in specific cases. Say that you have an application that edits contacts in the browser. Whenever you add an attachment to the contacts, you want to register the attachment with a display panel that shows contact statistics

The contacts editor and the contact statistics panel are not aware of each other, so there is no direct way for the editor to tell the display panel that the attachment has been added to the contact. Using traditional event bindings, you could register an even listener to the browser document object, or you could use tiny-pubsub to subscribe to a "contact.addAttachment" or "contact.save" event where you could handle the updates to the attachments.

 

A more targeted solution

Rather than broadcasting an event and chaining everything to it, what if you could, in the display panel, register an event listener against the contact, providing syntax and functionality similar to jQuery's $.on() event handling? That way, you could register any number of event handlers from any number of components. The ui components would not know about these interactions or be involved in them.

 

Event bindings

It turns out not to be that difficult to bind methods to objects in JavaScript. First, let's create an EventBinding object that contains methods to bind, unbind, and fire events.

var EventBindings = {
    on : function( event, func ){
        if( this.events[ event ] === undefined ){
            this.events[ event ] = [];
        }
        this.events[ event ].push( func );
        return this;
    },

    off : function( event ){
        // clear listeners
        this.events[event] = [];
        return this;
    },

    fireEvents : function( key, args ){
        var ix, func;
        $.each( this.events[ key ], function( ix, func ){
            func( args );
        });   
    },

    getAll : function(){
        return [ { name : "on", func : this.on },
                 { name : "off", func : this.off },
                 { name : "fireEvents", func : this.fireEvents } ];
    }
};


Using these methods, I can register event handlers against an object, execute methods on those events, and unregister them. But I need a way to get these methods into my target objects.

 

Enter custom constructors

Using a custom constructor, I can generate objects from their prototypes and add arbitrary functionality through the use of mixins:


function Constructor( constructor, args ){

    var prop, ix, returnedObj, obj, bindings = EventBindings.getAll();

    // add bindings to the prototype
    for( ix = 0; ix < bindings.length; ix++ ){
        if( constructor.prototype[ bindings[ ix ].name ] === undefined ){
            constructor.prototype[ bindings[ ix ].name ] = bindings[ ix ].func;
        }
    }

    //generate an object - Doug Crockford's technique
    obj = Object.create( constructor.prototype );

    returnedObj = constructor.apply( obj, [ args ] );
    if (typeof(returnedObject) ===  'undefined') {
        returnedObj = obj;
    }

    // add an events collection to the new object to track event handlers
    returnedObj.events = [];
    // create an event key for each method in the prototype
    for( prop in constructor.prototype ){
        returnedObj.events[ prop ] = [];
    }

    // get the event binding methods
    var bindings = EventBindings.getAll();

    // add the event binding methods to the generated object
    for( ix = 0; ix < bindings.length; ix++ ){
        returnedObj[ bindings[ ix ].name ] = bindings[ ix ].func;
    }

    return returnedObj;
}

 

Using this Constructor method, I can generate an object that contains its own methods and the methods of the EventBindings object, thus enabling binding to methods on the object. If I want to enable binding for a particular method, all I need to do is add a line to the prototype method to call fireEvents:

 

Contact.prototype.addAttachment = function( item ){
    var self = this, ix, func;
    this.attachments.push( item );

   this.fireEvents( "addAttachment", item );
};


Now I can chain a method to the addAttachment method as an event listener:

contact.on( "addAttachment", function( item ){
    // add functionality needed in this component.
});

 

Event listeners can be added at any point in the object's existence and anywhere the object is accessible. Multiple event handlers can be added, offering flexibility where the code for an event handler is located.

I am using the terms events and event listeners and handlers, though in the strict sense I am not using Event objects in this example. You could extend the functionality to include an Event object if that suited your needs. You could also wrap all the prototype methods in your target object using a wrapper that called the fireEvents method after executing the target method. That would free you of the need to add fireEvents to each method call, though it would potentially add a lot of extra method calls for little gain if you only plan to use the event bindings where you need them.