Prototypes in JavaScript
My previous article, Prototypes and Object Orientation, considered the differences between class-based and prototype-based object orientation. In this article, we'll look in a bit more detail at the workings of the JavaScript object model, since it's currently the most popular prototype-based object-oriented language.
What's New?
In Self, you create a new object by sending a message to it that creates a clonean object that delegates everything to the original object. By contrast, JavaScript was designed to have syntax similar to Java's. Unfortunately, this difference makes JavaScript quite a perplexing language in a lot of ways, because it uses Java-like syntax for Self-like semantics. People who are familiar with Java get confused because things don't behave as they expect.
- In Java, you create a new object by using the new keyword, followed by the name of a constructor. The constructor is a special method on a class that handles initialization, and it has the same name as the class.
- In JavaScript, the syntax is the samethe new keyword followed by the name of a constructor. The semantics are quite different, however.
In Java, the new keyword creates a new instance of the class and then calls the constructor to initialize it. JavaScript has no classes, and the constructor is just a function. It can be any functionalthough you may get some strange results if you call a function that's not designed to be used as a constructor via new. The new keyword creates a new empty object and sets its prototype to the object in the constructor function's prototype slot and its constructor slot to the constructor function. The function is then called with the local this variable (which points to the function object, when the function is called directly) set to the new object.
There's no official way of modifying an object's prototype after construction (although Mozilla provides the __proto__ slot as an extension for this purpose). You can implement a generic clone function very easily in JavaScript:
function cloneObject(obj) { var clone = function() {}; clone.prototype = obj; return new clone(); }
This example creates a new temporary constructor function that does nothing, setting its prototype slot to the object passed as a parameter to this function. It then calls the temporary constructor via the new keyword. This action creates a new object, with none of its slots other than constructor set, which delegates everything to the original.
If you want to implement a class-like model on top of JavaScript, for example, this approach can be very useful. You create a prototype object that has default values for all of its instance variables, and all of its methods are defined. Then you clone it to create a new instance. The instance will delegate all method lookups to the prototype.
This is how you write code in Self, and it's typically cleaner than the common JavaScript pattern of defining loads of things in the object's constructor. With a sensible prototype, you don't need to set anything in the no-argument version of the constructor.