Are You Sure You Should be Subclassing That?

Creating a subclass in JavaScript is an art form. You have to create a function, link the prototype of your function to the prototype of the superclass function, and then make sure that you call the superclass function, taking special care to make sure that it runs in the same context as your function.

Here’s your problem: you just want to change ONE property in this class, but you can’t change it on the actual class because that value would now be used across all instances of that class. So in order to make it happen, you go through all the messy, painful steps described above.

But wouldn’t it be nice if we could just do this:

var Child = sp.clone(Parent, { foo: "new value" });

It would certainly be a lot less painful and a lot prettier. By the end of this example, you’ll not only have this function available to be added to your code base, you’ll know how it works.

Disclaimer

I’m a firm believer in shallow inheritance when it comes to JavaScript. Every time it seems like I have to subclass something, I try to find a different, cleaner way to do it. Most of the time, there’s not only a way to do it without inheritance, but it ends up being cleaner, faster, and more maintainable.

I say this to give you an idea of why we’re going to do some of the things we do when creating this function. Also, I feel like applying this method to an already long and complicated inheritance chain is evil. You’ve been warned.

Getting Started

At the top of this page you’ll see me explain that the first thing you have to do is create a function. I said this because it’s true, and it’s the first thing we’ll want to do in our clone function. While we’re adding that, we’ll take that special care I mentioned and make sure that we call the superclass function in the same context as our function.

Our function’s context is provided to us through the variable this, and the list of arguments used to call the function are provided through the variable arguments. Since all functions have an apply method, and we can pass context through its first parameter and arguments through the second parameter, it seems like an ideal solution to making sure that we call our superclass properly.

So we’ve solved the first and last requirements for proper inheritance. Now it’s time for the most important part: the prototype. The prototype is so important because it’s an efficient way to share functions and properties. A function that I assign to the prototype of a class is shared as a reference across all instances of that class. It means that one function can be shared thousands of times, but only created in memory once. The prototype chain adds to this idea, by allowing us to string together a bunch of different prototypes. When we are looking for a variable with a certain name, we can move up this chain until we find it.

Another important part of this is that if one of these values changes, its value will be passed down through all of its subclasses, and into every instance of every one of these subclasses.

I’m using dojo.delegate here to create the prototype chain for me. The guts of this function aren’t important—all we need to know is that dojo.delegate creates a new object for us that, when used as our function’s prototype, enables the prototype chain to work. Skip forward to the end of the article if you want to pull down the actual source code for the helper functions used during this example.

Finally, we restore the constructor property back to the prototype. Without doing this, when we were looking to see what constructor was used to create this function, we’d find a value somewhere further up the prototype chain, probably finding our superclass’s constructor. Setting this here ensures that never happens.

I promised you that we were going to be able to add new properties to our subclass, so let’s go ahead and do it.

The dojo.mixin function will make sure that all the items passed in the mixins parameter are added to the prototype.

At this point, we have a simple, workable solution. But we’ve created a couple of problems. First of all, every time we clone a clone, we add depth on two counts. Instead of calling the original function, we’re calling a function that’s calling a function that somewhere up the line calls the original function. We’re also adding a level of depth to the prototype chain. If one of the properties we’re using is way up at the top of the chain, your web browser is going to have to spend more time looking each time you add to it.

By setting the variable __original__ on the function, if you decide to clone a clone, we’ll be able to tell, and get a reference back to that original function. By doing this, no matter how many times you clone, there will only be a single level of depth between you and the original superclass. The second thing was to destroy the prototype chain. Destroy?! Yes, what we’ve done is take everything in the prototype chain and flatten it onto an object. It means that we still get all the object references, but if any of the superclasses change a value, we won’t see it. Think of it as saving the current state of the prototype. Because of this, variable lookups are extremely fast; they’re always one level deep, and continuing to clone the object won’t increase that. I’ve added a flag so that you can decide how you want it to behave; the default is to leave chain alone.

There’s one last thing I want to add to make this perfect. Sometimes I want to add variables from another class, or variables from an object, in addition to the final overrides that I make. Let’s update the function so that we can pass as many mixins as we want, and the final argument will always represent whether we want to unchain the prototype or not.

// Use the original def, then modify, then use new def
var foo = new MyClass();
alert(foo.bar()); // Should show baz
foo.bar = function() {return ‘boo’}
alert(foo.bar()); // Should now show boo

There may be errors in the above code. My JavaScript is rusty ever since I started using Prototype to do most of the dirty work for me but you get the idea. If you want to override just one method (or even a few) then just override them on the instance. This is built-in functionality for JavaScript.

@Eric Anderson: That only modifies a single instance. The reason I wrote this post is for creating a duplicate of a class with changes.

What usually happens in large projects is that you use a class, and you take the approach that you just noted. Let’s say you override a function or variable in exactly the same way, or with a high level of shared functionality in lots of places in your codebase.

It makes sense to not have to modify many instances with exactly the same properties. Not only does it mean that changing one of these properties means you have to change it everywhere, it also means that you have to allocate memory for each function or property you create.

With this clone method, you can create a clone of the original class with the lowest common denominator of functions and properties, and keep cloning the clones until you reduce any repeated properties. And you can do it all with much less expense than you would have by doing it on the per-instance level.