Prototypes, Constructor Functions And Taxidermy

When overriding the base `Backbone.View` to create your own view types from the base views, there are some issues that you may run in to with inheritance. The inheritance of constructor functions can be broken in strange ways, and code that you override in the constructor or other base ModelView or CollectionView functions may not be called when you expect it to be. This can be a tremendously frustrating problem, as the code you write looks correct, but it does not fire like you expect.

Fortunately, the fix is simple. Unfortunately, it’s not obvious. Worse, though, is that the simple but not obvious nature of the fix often makes it look like the solution is unnecessary.

The Problem

The short version of the problem is that replacing the constructor method on Backbone.View.prototype doesn’t work. The prototype.constructor attribute is part of the JavaScript prototypal inheritance system, and isn’t something you should replace at all. Doing so is essentially trying to replace the type itself, but only replacing a specific reference to the type. Backbone provides the ability to specify a constructor in it’s type definitions, and it uses that function as the constructor function for the type that is returned. But this is only a convenience if not a coincidental naming of a method.

Taxidermy On Prototypes

Replacing the prototype.constructor is a bit like taxidermy – the end result of “stuffing” a dead bear may still look like a bear on the outside, but it’s not really a bear anymore. It just looks like one. In the case of prototype.constructor though, this is especially dangerous because you can break code that relies on type checking or prototypal inheritance features that look at the prototype.constructor.

Visualize this through code, again:

function MyObject(){}

MyObject.prototype.constructor = function(){};

console.log(MyObject.prototype.constructor);
console.log(MyObject.prototype.constructor === MyObject);

Prototype constructor replacement

The prototype.constructor is no longer pointing to MyObject, so the original MyObject “constructor function” is not being applied to the new object instance.

Super Solution

All of the problems associated with the prototype.constructor replacement may be a bit disheartening. But there is hope, and a fairly simple solution.

There are two things you will need, to solve the problem of overriding a Backbone object’s constructor function without any other code having to extend from it directly.

  1. A new type with the “super-constructor” pattern (where a type calls back to the prototype.constructor manually)
  2. A complete replacement of the base view who’s constructor you want to replace.

By creating a new type that calls back to the original type’s constructor, you can ensure the correct chain of inheritance and constructors is handled. Then to get your new type in place without forcing others to extend from your type, you will need to replace the type from which your plugin extends, prior to any other code using it.

The Super-Constructor Pattern

The constructor function of a Backbone object looks like it would be even easier to replace than a normal method. If you extend from Backbone.View, you don’t need to access the prototype. You only need to apply Backbone.View as a function, to the current object instance.

Once you have your type set up, you will need to replace the original type with your new type. By doing this, any new type that tries to extend from the original named type, will get yours instead of the original. But you can’t just replace Backbone.View directly.

// define the new type
var MyBaseView = Backbone.View.extend({
  constructor: function(){
    var args = Array.prototype.slice.apply(arguments);
    Backbone.View.apply(this, args);
  }
});

// replace Backbone.View with the new type
Backbone.View = MyBaseView;

The args line is in the super-constructor example to ensure compatibility with older browsers. Some versions of IE, for example, will throw an error if the arguments object is null or undefined, and you pass it in to the apply method of another function. To work around this, you can slice the arguments object in to a proper array. This will return an empty array if the arguments is null or undefined, allowing older versions of IE to work properly.

Unfortunately, this setup will fail horribly. When the call to Backbone.View.apply is made, it will find your new type’s constructor sitting in Backbone.View, causing an infinite loop.

Correctly Overriding The Constructor Function

To fix the view replacement and associated problems, you need to store a reference to the original Backbone.View separately from the new view type. Then you will need to call this reference from your constructor function, and not the Backbone.View named function, directly.

(function(){
  // store a reference to the original view
  var Original = Backbone.View;

  var MyView = Original.extend({
    // override the constructor, and all the original
    constructor: function(){
      var args = Array.prototype.slice.call(arguments);
      Original.apply(this, args);
    }
  });

  // Replace Backbone.View with the new one
  Backbone.View = MyView;
})();

Now the Backbone.View has been replaced with your view type, while still maintaining a reference to the original so that it can be called when needed. Provided the file that includes this code is loaded prior to any other code extending from Backbone.View, all views will receive any functionality defined in MyView – which is exactly what you wanted in the Automation ID view.

An Excerpt From Building Backbone Plugins

This blog post is an excerpt and preview of Chapter 5 in my Building Backbone plugins e-book. The complete chapter includes a more meaningful problem statement and solution set: trying to inject behavior in to a Backbone application without requiring any part of the application to know about the new behavior. It’s an interesting and common problem, and the solution as shown here is simple but not at all obvious. 

For more information about the e-book, including additional samples of the content, head over to BackbonePlugins.com.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in AntiPatterns, Backbone, Books, E-Books, Javascript, Prototypal Inheritance. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Rajesh Segu

    http://www.rajeshsegu.com/2012/06/js-extend-a-method/

    I have a solution adding a ‘super’ attribute, this will make sure you need not hardcode your super method in code.

    • http://mutedsolutions.com Derick Bailey

      definitely a cool solution. i’ve seen other things like this, too. i think backbone even has something like this built in – but it’s one of those things where the official word from the backbone team is that you shouldn’t use it. i don’t remember why, off-hand. I know other frameworks support stuff like this, though.

  • Jacob Wadsworth

    Awesome article! Backbone plug-in gets me dizzy. The process of it is difficult for me to understand. Do you have video tutorials? I really have to buy that book for me to understand it more.

    -http://www.jdidproductdesign.com/

  • http://www.games-yepi.com/ Yepi

    Thank you for sharing, I have much more information from the article

  • http://ifandelse.com Jim Cowart

    You can actually accomplish this without needing to set aside a ref to the original Backbone.View. The original Backbone.View constructor will be on the prototype, thanks to the nature of JavaScript, which let’s you do stuff like this: http://jsfiddle.net/ifandelse/NJYKv/

    I’ve fortunately never had to deal with deep inheritance hierarchies with Backbone views – I prefer a mixin style approach for composition. I’m sure some people would be purists about wanting to see __super or something like that in their derived constructor, but I actually like seeing the ref to the View.prototype.constructor (or whatever the base happens to be), since that immediately gives me an idea of what was inherited (and since there’s not a language-level facility to say “inherits” or “implements”….anyways, food for thought…

    • http://mutedsolutions.com Derick Bailey

      that pattern works for standard inheritance… but if you’re *replacing* the original view with a new view type, then you had to set aside the reference to the original.

      var MyView = Backbone.View.extend…;
      Backbone.View = MyView;

      this does funny things with infinite loops if you just use the pattern your fiddle shows. :D

      • http://ifandelse.com Jim Cowart

        I guess I’m curious as to why you’d want to nuke the original Backbone.View rather than derive from it with an extended constructor. Feels like monkey-patching constructor functions in a commonly used library – and could lead to lots of WTF moments via the PLS violations….

        • http://mutedsolutions.com Derick Bailey

          the short answer is adding default behavior for all view instances. the example i use in my book is adding a “data-automation-id” attribute to every view instance, for easier test automation. think aspect-oriented-programming where you can inject and remove behavior as needed, depending on the environment you’re in :)