Caffeinated Simpleton

Cobra: A Little JavaScript Class Library

In my last post, I talked a bit about the problems I saw with trying to express new types and objects in JavaScript. It’s not so much that anything is difficult to do, it’s that doing different things requires very different (and sometimes very verbose) syntax. I tried a few different things to trick JavaScript into behaving differently, but in the end, I realized that perhaps just keeping things simple was the best thing to do. I wrote down a few small features for my little library.

  • Classes. Not really the full blown classes of Java lore, but more a shortcut way of doing ClassName.prototype.functionName = function(args).
  • Inheritance. I wanted my classes to support the prototypal inheritance built into JavaScript.
  • Singletons. I wanted to be able to create single instances of classes without leaving a class around that might confuse people.
  • Namespaces. I eventually realized that Object was good enough for this.

I also wanted some convention for how to deal with private methods and properties. In JavaScript, it’s pretty easy to have private members, and it’s pretty easy to do prototypal inheritance, but it’s a bit messier to do both.

My final bit of inspiration was Python. Python doesn’t care about privacy, it just trusts the developer not to mess things up. I like this philosophy. I also like the explicit self in python methods.  A lot of people don’t like self, but I think it makes it very obvious what instance your code is acting on. I took these things to heart and wrote Cobra, a class system for JavaScript that’s really simple and looks a whole lot like Python. Without further ado, let me show you some examples of Cobra.

Classes

/* This is our base class. In its initialization function,
 * all it does is set things that are true for
 * all living animals.
 */
var Animal = new Class({
    __init__: function(self) {
        self.breathes = true;
    }
});
 
/* Feline extends animal and overrides it's initialization function.
 * Notice that it calls it's parents __init__ function, just to be safe.
 */
var Feline = new Class({
    __extends__: Animal,
    __init__: function(self) {
        Class.super(Feline, '__init__', self);
        self.claws = true;
        self.furry = true;
    },
    says: function(self) {
        console.log ('GRRRRR');
    }
});
 
/* This is a cat. It inherits from Feline, and therefore also inherits from
 * Animal. It says something a bit different from most felines.
 */
var Cat = new Class({
    __extends__: Feline,
    __init__: function(self) {
        Class.super(self, '__init__', self);
        self.weight = 'very little';
    },
    says: function(self) {
        console.log('MEOW');
    }
});
 
/* Tigers are like most Felines except that they weigh a lot.
 */
var Tiger = new Class({
    __extends__: Feline,
    __init__: function(self) {
        Class.super(Tiger, '__init__', self);
        self.weight = 'quite a bit';
    }
});

If we try using these classes, you’ll see that they work as they should:

>>> sneakers = new Cat();
Object breathes=true claws=true furry=true
>>> sneakers.breathes
true
>>> sneakers.claws
true
>>> sneakers.furry
true
>>> sneakers.weight
"very little"
>>> sneakers.says();
MEOW
>>> tigger = new Tiger()
Object breathes=true claws=true furry=true
>>> tigger.says()
GRRRRR

You’ve probably noticed something very strange at this point (at least for the JavaScript world). Every instance method takes “self” as its first parameter. This parameter is the instance. Whether “this” is currently bound to the window or any other random object, “self” will always be the instance of the class. It’s a nice property, and you can still use “this” however you may wish. Just remember that your methods have to take “self” as their first arguments or weird things will happen.

Singletons

As I mentioned in my last post, I’ve had some issues creating singletons in JavaScript*.There is more than one way to do it, and you have to change a whole lot to go from one form to the other. If you no longer want your object to be a singleton, that might be a whole new refactoring pain. So I created a simple Singleton class that uses the same syntax as the Class type above, but it immediately discards its class and returns a single instance. This is nice because creating it is exactly the same as creating a class. You don’t have to think about it at all, and if you eventually realize a singleton was a bad idea, it’s trivial to convert it to a class.

var sanFranciscoZoo = new Singleton({
    __init__: function(self) {
        self.cats = [new Tiger(), new Tiger(), new Cat()];
    }
});

That’s pretty much it.

Namespaces, statics, and privacy

Everything else needs to be solved by convention. By looking at a method, it’s easy to tell whether it is static or not. If “self” is the first parameter, then it’s not static. So how do you create static methods? Well, for now, you don’t. If you have a group of static methods, stick them in an Object, which can be used as a namespace. You can even do the equivalent of C++’s “using”:

with (MyApp.Utils) {
    //this would normally be referenced as MyApp.Utils.utilityFunction();
    utilityFunction();
}

How about privacy? It’s handled the same as Python. Everything stuck into “self” is public, so use a leading underscore to indicate that others shouldn’t touch that data or method.

var Secretive = new Class({
    __init__: function(self) {
         self._doNotTouch = 5;
    }
    _doNotCall: function(self, add) {
        self._doNotTouch += add;
    }
});

I know that a lot of people don’t like the underscore approach (including JavaScript hero Doug Crockford), but it’s simple, consistent, and clear. I’m stickin’ with it.

Try It

I would love it if people would try it out on their own and tell me what they think. The source is available on bitbucket. Any problems can be reported in the comments or be filed here. There certainly might be some, I just whipped this up a few hours ago.

*When I say singleton, I mean a single instance of a class. In the design pattern world, this means that doing “new MySingleton()” would always return the same instance. I find that deceptive, so I just want it to throw an error when you try to make a “new” singleton.

Tags: , ,

This entry was posted on Monday, January 12th, 2009 at 1:52 am and is filed under Development. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

  • This looks awesome. Crescendo needs some work for the web client, so there's a distinct possibility of using Cobra for that. I think it has a lot of potential, you definitely nailed down something that's been needed for awhile without the bloat of a huge framework.

    Nice work.
  • You probably would want to use another library in addition to this. It plays nicely with other libraries, even ones that already have a "Class" object (you just need to refer to Cobra.Class instead). I like MooTools when I don't need ExtJS, but everybody else seems to love jQuery.
  • This is nice. Please consider dropping the underscores on init and extend. Also Ruby developers like me would prefer initialize to init. Great work.
  • There is a reason for the underscores. The underscores indicate "this is special, and the system will do something different with this parameter". I'm all for other suggestions, but I would like to maintain some sort of visual differentiation between normal attributes and "special" attributes.

    I'm a python developer, not a ruby developer, so init's going to be sticking around :).
  • Might clean things up a bit if __extends__ could be defined implicitly as the first arg to Class. Also, with something like Resig's makeClass you could drop the `new` keyword: http://ejohn.org/blog/simple-class-instantiation/

    Thus you'd get:

    var Cat = Class(Feline, {
    __init__: function(self) {
    Class.super(self, '__init__', self);
    self.weight = 'very little';
    },
    says: function(self) {
    console.log('MEOW');
    }
    });

    Interesting work, I'll be keeping my eye on the project.
  • I thought about both things and eventually decided against.

    I wanted __extends__ to be a member of the class declaration so that it's explicitly clear what is going on. There's no concept in JavaScript that would imply that by passing a different class as the first parameter, a new class that inherits from that parent class should be created. Any such implication is derived from how other languages do things. Thus, to avoid any possibility of confusion, I explicitly label the parent class.

    I also thought about getting rid of "new". In fact, I got rid of it before deleting all that code and leaving it in. There are a few reasons to keep new.

    1. This is JavaScript. A new instance of a class is created by using the "new" keyword. I did not want users of classes created with cobra to be forced to alter how they use JavaScript.

    2. I want there to be one (and only one) obvious way to instantiate classes. It's easy to make the "new" keyword optional, but then you have two ways to instantiate classes. The way I have it, there is one way, and that one way is obvious.

    3. More code. The first two reasons are pretty good I think, so why write more code to support a feature that is not necessary?
  • I'm playing with it right now in combination with jQuery and am quite impressed thus far. I've been wanting class functionality like this for a while, dabbled with Moo Tools, but could never quite leave jQuery. The two work great together so far and are making the development of this new functionality a snap. Thanks a ton, I'll let you know more as I use it :)
  • Sweet! Considering I haven't used it in a real project yet, I'm glad to hear that it's working for you. Let me know if you have any problems!
blog comments powered by Disqus