Javascript Inheritance to protect your variables

Javascript inheritance is a multi-horned beast. You can use classical inheritance to make things work a little bit more like Java, or you can subscribe wholeheartedly to doing things Javascript’s way.

Doing things Javascript’s way can be a boon, once you wrap your head around it. You don’t need any helper functions to help you out with this. You don’t need a library to implement useful inheritance, like jQuery or Prototype. You gain the benefits of being able to define private variables for your objects. You do lose a little bit in terms of memory usage. But these days, memory is cheap - and Javascript engines are only getting faster. So why not make it easy on yourself, as a developer - and your users, as downloaders?

You’re also limited in that it’s difficult for an overridden method to access the object’s parent implementation. However, I’m firmly of the belief that if you need to do that, you’re doing it wrong. So, without further ado, let’s delve into how to use Javascript to inherit in style - the Crockford Way.

Our base class is simple. Taking cue from the multitude of other object oriented examples floating around on the ‘net, I’ll be using animals to exemplify this process. If you need a primer on what inheritance actually means, feel free to peruse Wikipedia’s article on inheritance.

So, we start with a simple Animal class. It can’t do much. It does provide a nice framework for us to build other classes off of.

  1. var Mammal = function(name,numberOfLegs) {
  2.     this.getName = function() {
  3.         return name;
  4.     };
  5.     this.getNumberOfLegs = function() {
  6.         return numberOfLegs;
  7.     };
  8.     this.speak = function() {
  9.         alert("This animal is mute…!");
  10.     };
  11. };

Pretty basic class. We can instantiate it, and call some of its member properties like usual:

  1. var PrimoridalAnimal = new Mammal("Goo",0);
  2. alert(PrimordialAnimal.getName()); // "Goo"
  3. alert(PrimordialAnimal.getNumberOfLegs()); // "0"
  4. PrimordialAnimal.speak(); // alerts "This animal is mute…!"

Not a very useful animal! Let’s define what a cat looks like.

  1. var Cat = function(name,color) {
  2.     this.getName = function() {
  3.         return name;
  4.     };
  5.     this.getNumberOfLegs = function() {
  6.         return 4;
  7.     };
  8.     this.getColor = function() {
  9.         return color;
  10.     };
  11.     this.speak = function() {
  12.         alert("Meow!");
  13.     };
  14. };

Uh oh. There’s quite a bit of code duplication here. The getName function is pretty much the same as Mammal’s - and the getNumberOfLegs simply returns a static integer! The only truly new things we have here are the speak method and the new attribute, color. Let’s see if we can create a Cat object that inherits from the Mammal object.

  1. Cat = function(name,color) {
  2.     var parent = new Mammal(name,4)
  3.     parent.getColor = function() {
  4.         return color;
  5.     };
  6.     parent.speak = function() {
  7.         alert("Meow!");
  8.     };
  9.     return parent;
  10. };

There we go! We aren’t really returning a “Cat” object here - we’re modifying a new instance of the Mammal object with some new methods we can use. Note the continued use of closures to provide us with the private member, color. Note also, that unlike many methods of JavaScript inheritance, we can actually pass parameters to the parent object. That’s fairly useful! Let’s see how we can instantiate and use this new object:

  1. var yvonne = new Cat("Yvonne","calico");
  2. alert(yvonne.getName()); // alerts "Yvonne"
  3. alert(yvonne.getColor()); // alerts "calico"
  4. yvonne.speak(); // alerts "Meow!"

Works like a charm. To extend this metaphor a little bit further, let’s make a lion.

  1. var Lion = function(name) {
  2.     var parent = new Cat(name,"Tan, with a tuft on the tail!");
  3.     parent.speak = function() {
  4.         alert("ROAR!");
  5.     };
  6.     return parent;
  7. };

Now, to experiment with the output:

  1. var billy = new Lion("billy");
  2. alert(billy.getColor()); // alerts "Tan, with a tuft on the tail!" - how cute!
  3. billy.speak(); // alerts "ROAR!" .. yikes!

Fairly useful compared to some of the other paradigms out there, if you ask me.

Now, you remember I mentioned the memory issue as a minor issue, in the introduction? I’d like to take a little aside to explain why.

When we decorate the parent class, we’re creating new methods. We aren’t utilizing the prototype of, say, the Cat object. We’re attaching new instances of functions directly to the Mammal object. For each new cat object, there are two more functions floating around out there taking up space. With new advances in JavaScript interpreters and with how cheap memory is, though, this shouldn’t be that much of an issue. If I’d have to guess, I’d say this method is actually faster, in terms of CPU time, than the methods put forth in most of the libraries out there. I’ll follow up with a benchmark at some point - it’s a curious question, that’s for sure.

In the meantime, though, loosen your brain and write your JavaScript the most natural way it is for you. This just happens to be my preferred method of inheritance. I’d love to hear why you prefer Prototype or YUI’s method over this, though!

To make your object structure even more powerful, take a look at my article on Javascript interfaces.

Update: I did some benchmarking with Robert’s JSLitmus test suite, and this method is indeed faster on execution. It is a little slower on instantiation, however.

Reblog this post [with Zemanta]

Your email:

 

Tags: , , , , ,

2 Responses to “Javascript Inheritance to protect your variables”

  1. I’d really like to see your statement, “I’d say this method is actually faster,” quantified… ’cause, I think that may be making a pretty big assumption. (Hint: I created JSLitmus to make exactly this sort of testing as easy as possible ;) )

    Also, note that your constructor doesn’t have to create new functions. Just combine the module pattern and a method like Prototype’s Object.extend() as follows:) here …)

    var Cat = (function() {
    var methods ={
    getColor: function() {
    return color;
    },
    speak: function() {
    alert(”Meow!”);
    }
    };

    return function(name, color) {
    var parent = new Mammal(name,4)
    return Object.extend(parent, methods);
    };
    })()

    (P.S You might install WordPress’ Subscribe2 plugin to make it easier for comments to stay in touch with any discussion your posts may generate.)

  2. Jonathan Prins says:

    Thanks for the comment, Robert!

    I’m actually working on a benchmark for it right now - I’ve been fairly busy with my day job since posting this, so it hasn’t quite happened yet. I was going to add it to the inheritance benchmark on your blog - but then I realized you were doing something completely different with that. Comprehensive comparison to other libraries is forthcoming though, I promise! In retrospect, I probably shouldn’t have posted this without the figures ;)

    Your code, however, doesn’t work ;)

    You’re referencing the variable “color” expecting to be able to access it, however you cannot:

    The first reference to “color” is in the return constructor method. While that constructor method may have access to variables defined in encapsulating functions, the opposite is not true: “color” is actually defined in a completely different closure than it’s used in your example.

    A more readable version of the prototype method of extending, while still passing parameters to the parent object:
    var Cat = function(name, color) {
    var parent = new Mammal(name,4);
    return Object.extend(parent, {
    getColor: function() {
    return color;
    },
    speak: function() {
    alert(”Meow!”);
    }
    });
    };

    But the syntax is still wonky, though. Not nearly as succinct as I’d like it. I’ll work on some benchmarks to back up my claims on my flight tonight! (and maybe work out a way of accessing superclass methods from the subclass).

    Thanks for the tip on Subscribe2 - it’s on now!

Leave a Reply