Dynamic languages seem to be the flavor of the month in programming languages. And, why not? They enable fast development, concise code, and freedom from the rigid static type systems of Java or C#. Everyone, for example, seems to be raving about Ruby.
Prototyping is an idea worth understanding, because it will help you think beyond traditional object oriented programming. Prototyping and class-based inheritance can be seen at a higher level as two variants of the same general concepts. Simply put, prototyping is inheritance without classes.
Classes = delegation + interfaces
Like class-based inheritance, prototyping is a shorthand form of delegation. When one object delegates to another, the first object may simply pass a method call on to its delegate or may perform some additional functionality before or after the call. Those familiar with design patterns will recognize the Decorator pattern.
Not to get too far off track, but a lot of the power of aspect oriented programming boils down to a type of decorator usually called an interceptor in that context. The common ingredients here are delegation and a common interface.
To see that subclassing is a form of delegation, picture an instance of a Java class CodeMonkey, which is a subclass of Employee. Simple enough. Call a method on an instance of CodeMonkey and (conceptually) we first check if the method is defined in the class. If not, we delegate the handling of the call to the superclass Employee. Employee, in turn, can delegate to the class Object, which is the root of the inheritance hierarchy.
Subclassing defines an is-a relationship. A CodeMonkey is an Employee. In practical terms, this means that there is an implicit common interface implemented by both Employee and CodeMonkey, namely the interface of the superclass Employee. This is required for polymorphism. We have to be able to use a CodeMonkey anywhere we could have used an Employee.
There is something of a weird dichotomy between classes and objects. It doesn't seem weird because we're used to it, but think of all the awkwardness around reflection. That comes about because sometimes you want to treat a class as an object, while usually it's something quite different.
In Java, methods belong to the class, while instance variables belong to the object. Two instances can't have different implementations of a method like they can have different values for a member variable. Imagine, then, how all this might work in a language with no classes, only objects.
Prototypes, a shorthand for delegation
So how do prototypes mix with dynamic languages? Polymorphism can be taken for granted in a dynamic language. By virtue of duck typing, we don't need to worry about explicitly defining common base classes or interfaces. If the method walks like duck, we can call it. So, in most dynamic languages polymorphism is decoupled from type.
Prototyping decouples inheritance from type. In a dynamically-typed language, why expend effort to define type hierarchies? Dynamic languages are supposed to free us from worrying excessively about types. Certainly, delegation is still worth-while. Interfaces, though implicit, remain important. But what benefit do classes bring to the table?
Prototypes and dynamic languages
The popularity of class-based dynamic languages is due more to familiarity than function. Fluency in dynamic languages entails a shift in thinking of at least the same magnitude as going from procedural languages to OO. In a few years, building class hierarchies in dynamic languages may look as silly as the fortran-written-in-Java or C++ foibles that were so common not too long ago.Prototyping fits much better than classes with the ethos of dynamic languages. It provides the benefits inheritance without the baggage of types. Like class based OO, prototypes are something you have to work with to get a feel for. Once you do, you'll appreciate its flexibility as a shorthand notation for delegation.
The gang-of-four advise us to favor composition over inheritance precisely because of the extra baggage carried along by subclassing. In the context of dynamic typing, prototype based inheritance offers a middle road, uncluttered by type hierarchies. Prototyping and dynamic languages - two great tastes that go great together!