Monday, June 01, 2009

Prototypal Inheritance in Javascript - Template Method meets Strategy

I have been reading some of the papers on Self, a programming environment that models computation exclusively in terms of objects. However unlike the classical object-oriented approach, Self is a classless language, where everything is an object. An object has slots - each slot has a name and a value. The slot name is always a String, while the value can be any other Self object. The slot can point to methods as well, consisting of code. A special designated slot points to the parent object in the hierarchy. Hence each object is consistently designed for extensibility through inheritance. But since we don't have class structures, everything is dynamic and runtime. Objects interact through messages - when an object receives a message, it looks up into its slot for a match. If the matching message is not found, the search continues up the chain through successive parent pointers, till the root is reached.

Prototype based languages offer a different way of implementing objects, and hence require a different thinking for structuring your programs. They make you think more in terms of messages that your objects will receive, and how the messages get propagated up the inheritance chain.

Javascript follows an almost identical architecture, where the hierarchies of objects are constructed through prototypes. This post is not about Self or, for that matter, about the Javascript language. Some time back I had blogged about how the Template Method design pattern gets subsumed into higher order functions and closures when implemented using functional programming languages.

In a class based language, template method pattern is implemented in terms of inheritance, which makes the structure of the pattern static and makes the derivatives of the hierarchy statically coupled to the base abstraction. Closures liberate the pattern structure from this compile time coupling and make it dynamic. But once we take off the class inheritance part and use higher order functions to plug in the variable parts of the algorithm, what we end up with closely matches the Strategy pattern. Have a look at James Iry's insightful comments in my earlier post.

James also hinted at another level of subsumption which is more interesting - the case of the two patterns implemented in a prototype based language like Javascript. Here is how it looks ..

// the template function at the base object
// defines the generic flow
// uses hooks to be plugged in by derived objects

var processor = {
  process: function() {
    return true;

We construct another object that inherits from the base object. The function beget is the one that Douglas Crockford defines as a helper to create a new object using another object as the prototype.

if (typeof Object.beget !== 'function') {
  Object.beget = function(o) {
    var F = function() {};
    F.prototype = o;
    return new F();

var my_processor  = Object.beget(processor);

The new object now implements the variable parts of the algorithm.

my_processor.doInit = function() {
my_processor.doProcess = function() {
my_processor.doEnd = function() {

and we invoke the function from the base object ..


If we need to define another specialization of the algorithm that only has to override a single variable part, we do it likewise by supplying the object my_processor as the prototype ..

var your_processor= Object.beget(my_processor);
your_processor.doEnd = function() {
  //.. another specialization


So what we get is a dynamic version of the Template Method pattern with no static coupling - thanks to prototypal inheritance of Javascript. Is this a Template Method pattern or a Strategy pattern ? Both get subsumed into the prototypal nature of the language.


Martin Dobmeier said...


what would you say are the main differentiators between class-based OO and prototype-based OO, i.e. what properties of a prototype-based language actually make into one? Is it "just" the ability to create/modify prototypes at runtime together with this very dynamic relationship between them? Well, I'm never a 100% sure about the exact distinction so it's great that you brought up the subject ;)

Cheers, Martin

Unknown said...

In class based languages, inheritance imposes strong static coupling between classes. It is possibly the second most strongest coupling after 'friend' classes of C++ ;-). And people often tend to use inheritance for code reuse only even when there is no proper is-a relationship. The delegation approach in prototype based languages is much more flexible, since it is dynamic. What u say is perfectly ok, but for a more detailed explanation, have a look at this paper .. Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems

Sam Aaron said...

I think you're absolutely right that the prototype based approach can more flexibly subsume many of the class-based OO patterns out there.

Having played with Ioke a lot recently in my spare time (in addition to programming in Ruby at the office) I really have started to perceive classed-based inheritence as a cumbersome and overweight set of functionality compared to the much leaner and flexible prototype approach. The overweightness I refer to is a conceptual one. Ruby is particularly bad in this department; anybody who describes themselves as a 'metaprogrammer' will be perfectly aware of the inherent complexity of the combination of classes, modules and metaclasses. This is highlighted by observing the difficulty of the task of explaining the difference between instance_eval and class_eval.

Obviously, there are cases where the class-based system is an excellent fit for the current problem domain. However I don't believe that it is as common as people might think and in those cases building a class-like-system in a prototype language isn't an overly complex task. Therefore I believe that a class-based system as the default is a high tax to pay for just those particular cases.

Russell Allen said...

Hi Debasish, a better link for Self is the current homepage at - you can download it for Mac and Linux.