It's been said many times but bears repeating: JavaScript is an underappreciated language. Most serious programmers spend time working outside or on the fringes of the world of web development and so don't have a real use for it, while most web developers don't have the theoretical background to appreciate the fact that JavaScript is actually a wonderful language. But with all this Web 2.0 brou-ha-ha it's important to understand the philosophy of JavaScript.
One of the strangest features to programmers who are familar with other object-oriented languages is that JavaScript appears to only support a handful of their features, and those that it does support requires some strange syntax. It's not clear at all whether JavaScript supports any kind of encapsulation or even inheritance! Where, for example, are classes? Scandalous! And yet, all the rumors are true: JavaScript doesn't have a built-in notion of a "class," has no syntax for enabling classical inheritance, and provides no real support for encapsulation.
"Jesse," you might be thinking, "if JavaScript is supposed to be object-oriented but doesn't support these standard, almost universal, features, how can you possibly be calling it a 'wonderful language?'" This critique is unfair. Languages should be judged on their own merits. LISP is not Ruby is not C is not Haskell is not JavaScript. The fact that people even try to level this critique is evidence that most people never try to understand JavaScript qua JavaScript. JavaScript supports all the features of object-orientation, but not through the usual class/instance mechanism. What's more, JavaScript is flexible enough that you can actually add support for the usual notions found in most other object-oriented languages. Let's see how.
Classes, Forms, and George Berkeley
Most object-oriented languages use classical inheritance. You define a hierarchy of abstract classes and instantiate them in the form of objects. For example, you might have a Cat class. An instance of Cat has all the properties held by all cats plus whatever properties are specific to a given cat, e.g., fur color, breed, or the number of toes on each foot. Changing any of these parameters does not make the cat instance any less of a Cat. The philosophically inclined reader might note that this corresponds directly to a realist metaphysics a la Plato or Aristotle. Using Ruby as an example, it would look something like this:
def initialize(name, breed)
@name = name
@breed = breed
end
def speak
puts "Meow, I'm a #{@breed} cat named #{@name}."
end
end
persian = Cat.new('Tom', 'Persian')
persian.speak
=> Meow, I'm a Persian cat named Tom.
JavaScript, though, eschews the classical realist school in favor of a hipper, nominalist metaphysics. There is no such thing as a class and everything is really just an instance. Objects inherit directly from other objects and whatever properties they have in common is totally incidental. Code speaks louder than words, so without further adieu
this.name = name;
this.breed = breed;
}
Cat.prototype.speak = function() {
alert("Meow, I am a " + this.breed + " cat named " + this.name + ".");
}
var persian = new Cat('Tom', 'Persian');
persian.speak();
There are three things woth noting. One, JavaScript supports closures. Two, functions are first-order constructs in JavaScript and are used as object constructors. Three, JavaScript uses prototype-based inheritance. For experienced programmers the rarest of the three is definitely the third, so let's focus on that.
Prototype-based inheritance is simple. Every object has a special field called the prototype which points to another object. If an object is asked for a field which it does not directly contain then it looks at the associated object in its prototype field. If that object cannot find the field then it does the same, and so on, until the hierarchy of objects is exhausted or the field is found. This gives the programmer a direct way to establish a hierarchy of objects: we create a Cat object (not class), point every breed of cat's prototype to the Cat object, and then point every specific cat's prototype to a breed's object. Since any object can be modified at will in JavaScript, at each stage the programmer can customize the specific object (e.g., setting persian.breed = 'Persian').
This is the idea behind prototype-based inheritance, but unfortunately JavaScript doesn't support the above mechanism directly, either. The above code, for example, uses the "new" keyword, which seems normal in a language with classes but strange in a prototype-based language. After all, what's the use of a "new" keyword if you're not instantiating any classes?
This is one spot where JavaScript is, in my opinion, inconsistant. Perhaps when people were coming up with the standards that defined JavaScript they thought it would be just too strange to travel so far off the beaten path when everyone was already used to classical inheritance. Using the fact that "new Foo()" returns an object whose prototype is Foo we can write the following1:
function F() {}
F.prototype = o;
return new F();
}
speak: function() {
alert("Meow, I'm a " + this.breed + " cat named " + this.name + ".");
}
}
var Persian = object(Cat);
Persian.breed = 'Persian';
var Tom = object(Persian);
Tom.name = 'Tom';
Tom.speak();
It wouldn't be hard now to create a generic factory function, which takes as its input a base object and a second object whose fields are used to overwrite fields in the base class, e.g.,
var o = object(base);
for (key in params) {
o[key] = params[key]
}
return o;
}
If you're feeling particularly object-oriented the above could be changed to
var o = object(this);
for (key in params) {
o[key] = params[key]
}
return o;
}
Tom.dance();
So, there you go, JavaScript nay-sayers. Not only can JavaScript provide for the functionality of other object-oriented languages, but has something to teach us with its own object model. Given the central role JavaScript plays in Web 2.0 it's definitely worth your time to learn these constructs and idioms.
- This comes from Douglas Crockford, who, as far as I know, was the first to give an extended exposition on these aspects of JavaScript [back]
[…] The Philosophy of JavaScript | 20bits Good article explaining the differences between JavaScript and other object-oriented languages. (tags: javascript) […]
Wow, very cool stuff.
I never really worked with direct objects in javascript and i have seen little code written like the above.
Thanks!
I covered this topic in my Feb, 2007 article: Implementing “Real” Classes in JavaScript
[see http://www.polyglotinc.com/AJAXscratch/ ] and later extended the technique to handle
multiple inheritance. A complete AJAX framework was developed using these techniques.
In learning how JavaScript *really* worked, I at first focused on how to emulate classical
classes (hence my articles above), but later I started thinking about the possibilities
opened up by JavaScript’s “class-less” programming. Having started reading Philosophy 101
books about that time, I realized that while traditional Object Oriented Programming is like
Plato and Aristotle (as you pointed out), JavaScript can be more like Existentialism.
I have started working on what that would mean and am recording my thoughts at http://ExistentialProgramming.com/
Actionscript (as used in Flash) is pretty much the same, I have used almost identical code to create classes in my flash presentations. Some differences, ‘prototype’ becomes ‘__prototype__’ for example. :)
(What on earth is the above comment about?!)