Advantages of prototypes
Classes are great conceptually, in that they are easy to understand and work with. Prototypes are not quite as easy to understand, but don't have some of the limitations of classes. Namely, it's painful to create one-instance-only objects (i.e. singletons) with classes, but it is quite natural to do this with prototypes.
Prototypes also let you modify objects' behavior at run-time, or create one-off instances. This is great for things like event handlers, where it's a lot of work to create a custom widget if you have to give it its own class. Newer languages offer anonymous classes and closures, but it's still awkward. Anonymous classes (Java) still have a lot of extra syntax if you just want to override one method, and delegates (C#) are better but still unwieldy.
The way I see it, there are two big advantages of prototype-based languages.* First, you can modify the behavior of many objects at once by modifying their prototype. For instance, you can add a method to a prototype and all the objects created from that prototype automatically gain that method. Second, you can modify the behavior of individual objects without the need for a one-off class.
I like the power of prototypes, but am more comfortable with the concept of classes. I'm not sure that prototypes-as-a-concept offer any real advantage over classes. Rather, I think the problem is the limitations built into class-based languages. If we were to remove these limitations, would prototypes still have anything to offer?
Drawbacks of prototypes
My main beef with prototypes is that necessity of some prototypical object to exist; you don't use that object, you just clone it to create new objects, the objects that you actually use. But then it seems like a waste to have all these prototypes sitting around not doing anything.
Consider a database object. With classes, you instantiate the database class to create a new database object, which connects to the database and lets you send queries. With prototypes, you have this prototypical database object already in existence. What does a "prototypical" database object look like? Is it connected to a "prototypical database"? Probably not. So can you actually do anything with it? If not, why should we pretend that this prototype is just as good as a real live instantiated-and-connected database object?
In the
overview
of the Self language, the authors compared class instantiation to building a
house from a blueprint, and prototype-cloning to just copying an existing house.
Yes, I suppose copying is a simpler metaphor, but it doesn't make sense as a
language-design metaphor. Copying requires that a source object already exist;
if we were building a house, it would be an awfully inefficient way for an
architect to design the house by building a full-scale model and then saying to
the construction company, "Here, copy that." Classes also seem more
natural when class instantiation requires one or more parameters.
Beefing up classes
Instead of throwing out the baby with the bathwater by ditching classes in favor of prototypes, why don't we give classes the strengths of prototypes? Above I listed two of them.
- You can give new properties and behavior to already-instantiated objects by modifying their prototype.
Javascript
2.0 has a beautiful, elegant system for allowing extensions to prototypes
while limiting the scope of such changes so that modules not expecting such
changes are unaffected. In essence, if you modify a prototype by adding or
modifying methods or attributes, your modifications are only visible within a
limited scope, such as the enclosing package.
Given such a system, it should be easy to add the same abilities to a
class-based language. One could, for example, add a new method
getSize to the System.Object class in Java. With a
sophisticated namespacing system, the method would only be visible in a limited
scope, and so wouldn't interfere with other getSize methods in
different classes.
- You can modify the behavior of existing objects
I don't see why one couldn't do something like
string message = "Hello world!";
message.rot13 = function() { ... };
message.rot13();
in a class-based language. We could mimic prototypes by searching for a method in the object upon which the method is invoked, and then the object's class if the object does not override the method.
Consequences of these changes
If objects could have methods added at run-time, this would impact static
type checking. In the code example above, the compiler could not declare the
message.rot13() statement invalid just by looking at the
string class.
To my mind, having these abilities would give classes the power of prototypes, but without the drawback. You could modify objects at will even after instantiation, like with prototypes, but would have the conceptually cleaner metaphor of instantiation rather than cloning.
Footnotes
*
Self, the
prototypical prototype-based language, claims other advantages, but I don't see
them as such. Self rightly did away with the distinction between variables and
methods, but C#'s use of properties shows that class-based languages can do
that, too. The creators of Self also write that it "allows for the
blurring of the differences between objects, procedures, and closures. Reducing
the number of basic concepts in a language can make the language easier to
explain, understand, and use. However, there is a tension between making the
language simpler and making the organization of a system manifest. As the
variety of constructs decreases, so does the variety of linguistic clues to a
system's structure."
Right on.