Picking up Javascript - Being classy
My current work at CakeDC allows me to be on IRC a lot – like all day. And while I’m often quite silent during the day, I do scan through a few PHP related channels and I’ve noticed a trend of PHP developers who just don’t get Javascript. Either they don’t have the experience, or they do have experience and think its icky. However, their clients want ‘ajax’ so they begrudgingly use this terrible language that doesn’t even have classes. Well recently I’ve been writing a pile of Javascript, in fact there have been quite a few days where I am spending more time in Javascript than PHP. So I wanted to talk about some of the things I’ve learned about the worlds most popular language.
No class.
While Javascript has class on its list of reserved words it doesn’t actually have an implementation of that keyword. This is a big obstacle for many developers coming from classical inheritance background. This combined with first order functions and javascript starts looking really strange to the PHP developer. One of the reasons that Javascript doesn’t currently have classes is that it is a prototype based language and it was originally modeled after Scheme and has more in common with functional languages like Lisp and Haskell than C family languages. This family of languages often doesn’t have classes as they are based on functions, and javascript is no different. The designers of Javascript intended the language to be used something like:
- var Person = function(name) {
- this.name = name;
- }
- Person.prototype.sayHi = function () {
- alert(this.name);
- }
This however is a little bit wierd and cumbersome looking for most people familiar with C family languages. But fear not, although Javascript has no classes doesn’t mean that it lacks a way to create the same type of code reuse that classes afford. The two primary approaches are by using prototypes and closures. Both offer ways to create objects that behave like traditional classes. Personally, I’m a fan of prototypal inheritance, and its also the approach that libraries like MooTools and PrototypeJS take.
Prototypes
Every object in Javascript has a prototype, this prototype describes what features (properties and methods) are going to be available on all instances of a given object type. Using prototypes as we saw above can be cumbersome but it has number of advantages over closures in my opinion.
First, adding features into an object’s prototype adds those features to all instances previously created, and those yet to be created. Now, let that sink in for a second. This is a huge and very important point. By using prototypes you have the ability to change objects on the fly, and add methods into existing objects, even the native objects. Furthermore, these changes will be updated automatically on all instances. In classical patterns, if you want to add a method to an object, you need to create a subclass and inject the subclass into the correct place. In Javascript you just modify the prototype.
- String.prototype.trim = function () {
- return this.replace(/^\s*/, '').replace(/\s*$/, '');
- }
Now the base String
object doesn’t have a trim()
method by default, however we can just add one in. All String objects will have a trim()
method after the above code executes. This is just a simple example of how much power prototypal inheritance affords.
So how does that work?
Prototypes in Javascript are checked each time a specific instance doesn’t have a particular method/property. So if you call myVar.foo()
where myVar
is an instance of Person
. First, the interpreter checks the instance for a foo()
method. If it doesn’t find it there it will recursively scan through all the prototypes for a foo()
method. If at the end of this, foo()
is still not found an error will be raised. Another interesting features of prototypes is that methods contained in an object’s prototype, are not bound to a specific instance and consume less memory than functions contained within closures.
So how does this make inheritance?
Well we’ve seen how prototypes allow you to augment an object type with new methods and properties. This augmentation is the basis of inheritance in Javascript. By leveraging prototypes you can take features from one objects prototype and add them to another, or tack on new features as needed. Both MooTools and PrototypeJS use this in their Class
objects. I have a simple version that I use when working with no library.
- var Class = function(features) {
- var klass = function(noStart) {
- if (typeof this.init == 'function' && noStart != 'noInit') {
- return this.init.apply(this, arguments);
- }
- return this;
- };
- for (var key in this) {
- klass[key] = this[key];
- }
- for (key in features) {
- klass.prototype[key] = features[key];
- }
- return klass;
- };
- Class.prototype.extend = function (features) {
- var oldProto, oldFunc, newFunc, func;
- oldProto = new this('noInit');
- var makeParent = function(parent, current) {
- return function () {
- this.parent = parent;
- return current.apply(this, arguments);
- };
- };
- for (var key in features) {
- oldFunc = oldProto[key];
- newFunc = features[key];
- if (typeof oldFunc != 'function' || typeof newFunc != 'function') {
- func = newFunc;
- } else {
- func = makeParent(oldFunc, newFunc);
- }
- oldProto[key] = func;
- }
- return new Class(oldProto);
- };
- Class.prototype.implement = function (features) {
- for (var key in features) {
- this.prototype[key] = features[key];
- }
- };
The above gives you a simple way to create prototypal inheritance in your Javascript. This approach is a simplification of the approach used in MooTools, and has been working well for me in everyday use. A sample use is :
- var Person = new Class({
- name : null,
- init : function (name) {
- this.name = name;
- },
- sayHi : function () {
- alert(this.name)
- }
- });
- var bob = new Person('bob');
- bob.sayHi(); //alerts 'bob'
- var Ninja = Person.extend({
- weapon : null,
- init : function (name, weapon) {
- this.parent(name);
- this.weapon = weapon;
- },
- strike : function() {
- alert(this.name + ' strikes with ' + this.weapon);
- }
- });
- var shinobi = new Ninja('ryu', 'shuriken');
- shinobi.sayHi(); //alerts 'ryu'
- shinobi.strike(); //alerts 'ryu strikes with shuriken'
- Ninja.implement({
- dodge : function() {
- alert('dodged!');
- }
- });
- shinobi.dodge(); //alerts 'dodged'
So there you go, a simple example of using inheritance and the idea of classes to contain functions together. In addition there was an example of using Class.implement
to tack features onto existing instances. Now classes based on prototypes are just one approach to creating and grouping related code. Another is to use closures, but closures are an entire subject on their own. So I hope that this helps with understanding javascript a bit. I hope to do more articles covering some of more of the interesting parts of Javascript including closures and creating inheritance and private members with closures, binding, and a pattern I’ve been using for organizing Javascript that has been working well for me.
hi,
I would suspect most developers are more afraid of/confused by the DOM, rather than needing this kind of explanation just yet.
http://video.yahoo.com/watch/4403981/11812238
mjc on 2/8/09
mjc: As Resig spoke of the DOM is a huge mess of inconsistent implementations, and broken features. I agree that this is a big stumbling block as well. However, the current Javascript libraries help smooth out these inconsistencies quite a bit.
mark story on 2/9/09
Great article Mark. You always manage to boil complex topics down in such a way that they can be more easily understood.
@mjc Yes the DOM is an issue. I’ll paraphrase what Mark said, “Unless you are a DOM expert or want to be one, then find a library you like and let it save you some headaches”.
Heath Nail on 2/9/09
Prototypes can be a hard thing to grasp, good job explaining it.
I think this:
bob.sayHi(); //alerts ‘hey hey’
should be:
bob.sayHi(); //alerts ‘bob’
with your example code.
Louis W on 2/9/09
Louis W: thanks for pointing that out. I added comments to the rest of the alerts as well.
mark story on 2/10/09
mjc: I don’t think the DOM’s issues make understanding JavaScript the language any less important.
First and foremost I think developers need to understand that the DOM is not JavaScript, it’s an API that the browser exposes to the JavaScript runtime.
nate on 2/10/09
Your Prototype link goes to http://prototypejs.com, where it should be http://prototypejs.org.
Gordon on 2/10/09
Gordon: Thanks :)
I think its also important to remember there are Javascript runtimes that exist without the DOM like Rhino. So its good to separate understanding of the language which i mostly good and not broken and the DOM which is a huge mess, but getting better all the time.
mark story on 2/11/09
excellent short article, this and “http://mark-story.com/posts/view/picking-up-javascript-closures-and-lexical-scoping”, and john resigs book “secrets of the javascript ninja” I am taking a close look at towards my study to be a more advanced JavaScript programmer. will take a look at your site ever so often for new articles…. Thanks!
Quinton Sheppard on 1/17/11