Aanand Prasad

Prototype Chains and CoffeeScript Subclasses

In developing nnnnext.com, I tackled a surprising number of problems I’d not faced before in my web development career. Here’s one of the more interesting ones, with what I think is a pretty cute solution.

Context, briefly: nnnnext is a todo list for your music. It’s a single-page JavaScript app written in CoffeeScript and utilising Backbone.js for lightweight MVC. Importantly, it has two similar-but-separate user interfaces: one for desktop web browsers, and another for multitouch devices.

Both interfaces are implemented as a set of classes (provided by CoffeeScript’s simple classes implementation) that represent the different UI views. Each UI requires slightly different behaviour for some views. For example, an element might contain a button that (in the desktop UI) appears when the element is hovered, and (in the touch UI) appears/disappears when the element is tapped. Consequently, some view classes are subclassed in one or both UIs in order to implement these differences. The class structure might look like this (implementation code omitted):

Views   = {}
Desktop = {}
Touch   = {}

class Views.Widget   extends Backbone.View
class Views.Thinger  extends Backbone.View
class Views.Sprocket extends Backbone.View

class Desktop.Widget extends Views.Widget

class Touch.Widget   extends Views.Widget
class Touch.Thinger  extends Views.Thinger

(Whatever you think of classical object-orientation and inheritance, it’s an undeniably good fit for MVC-style UI code—subclasses and the super keyword make augmentation of behaviour very straightforward1.)

So how, at runtime, might we instantiate the appropriate class? Running all instantiations through a helper method would certainly work:

# assume the existence of a `Mobile` boolean variable

makeViewObject: -> (name, args...)
  UI    = if Mobile then Touch else Desktop
  klass = UI[name] || Views[name]

  new klass(args...)

makeViewObject("Widget")   # => new Desktop.Widget OR new Touch.Widget
makeViewObject("Thinger")  # => new Desktop.Thinger OR new Views.Thinger
makeViewObject("Sprocket") # => new Views.Sprocket

…and if you’re most people, you’ll stop there. I, on the other hand, had just read Isaac Z. Schlueter’s Evolution of a Prototypal Language User, hadn’t done anything fun with prototype chains in years and didn’t like the idea of this unsightly helper method peppering my code. So I got to work.

Instead of Views, Desktop and Touch being plain objects, we create a ViewShop function, point Views at its prototype and make Desktop and Touch new ViewShops, so that property access falls back to ViewShop.prototype, which we’ve just aliased to Views… look, I’ll just show you:

ViewShop = ->

Views   = ViewShop.prototype
Desktop = new ViewShop
Touch   = new ViewShop

Now, after defining the view classes, access any property of Desktop or Touch, and prototype chaining works its magic:

Desktop.Widget   # => Desktop.Widget
Desktop.Thinger  # => Views.Thinger
Desktop.Sprocket # => Views.Sprocket

Touch.Widget     # => Touch.Widget
Touch.Thinger    # => Touch.Thinger
Touch.Sprocket   # => Views.Sprocket

We need only make a UI variable that points to the right ViewShop, and we’re done:

UI = if Mobile then Touch else Desktop

new UI.Widget   # => new Desktop.Widget OR new Touch.Widget
new UI.Thinger  # => new Desktop.Thinger OR new Views.Thinger
new UI.Sprocket # => new Views.Sprocket

Well anyway, I like it.

  1. I should be careful about using the word “undeniably” though. I’m no expert on object systems. If you feel you can reasonably deny it, I’d love to hear from you!