contact@delonnewman.name
profile software writing resume

Delon R. Newman

creating simple solutions to complex problems

Embrace Prototypal Object-oriented Programming

06/05/12

http://ibm.co/KfuDID

Object-oriented Programming
JavaScript

A World Of Objects

As we go about the world: driving to work, sitting at a desk performing a task, eating a meal, taking a walk through the park. We take for granted that we're able to manipulate and interact with our world without having to know the detailed physical laws that govern it. This alows us to treat the various systems we deal with from day to day as units, objects talking for granted their complexity and focusing, instead, on our interactions with them.

Object oriented programming is an attempt to create software systems that work the similarly, and thus, it has proven to be both a powerful and wildly popular modeling tool for software development. It's popular with developers because it reflects the way we view the world as humans--a collection of objects that can interact with each other and be manipulated in various ways. The power of object oriented programming is a reflection of the power of it's core principles: encapsulation (information hiding), and inheritance (information sharing). Encapsulation allows developers to conceal the inner workings of their data structures and reveal reliable programming interfaces that can be used to create modular, adaptable software. Inheritance allows developers to amplify the power of encapsulation by allowing objects to inherit the encapsulated behavior of other objects. At this point these principles are well known to most developers because basically (if not actually) every mainstream programming language supports object oriented programming (and in many cases they enforce it). What is less well known is that while all object oriented languages support these two core principles, in one form or another, over the years there have been at least two fundamentally different ways of defining objects.

A Note To Self

Starting with Simula, a language for modeling generally considered to be the first object oriented language, then Smalltalk, C++, Java, C#, and at this point most object oriented languages, objects are defined by class. Later, the developers of the Self programming language (a Smalltalk-like system) created an alternative, and lighter weight, method of defining objects, often described as prototype-based or prototypal object oriented programming. Eventually, JavaScript was developed with a prototype-based object system, and it's JavaScript's popularity that's brought prototype-based objects to the mainstream, something that many developers find distasteful. However, under closer inspection prototype-based systems have many advantages, but first let's start with a review of some familiar concepts.

Prototypo--What? (Classes and Prototypes)

A class provides an abstract definition for objects, defining shared data structures and methods for an entire class or collection of objects. Each object is defined as an instance of it's class. Classes are also given the responsibility of constructing class objects according to their definitions, and (optionally) by user parameters.

A classic example is the Point class and it's child Point3D for defining two dimensional and three dimensional points respectively. In Java they would look like this:

class Point {
    private int x;
    private int y;

    static Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    int getX() {
        return this.x;
    }

    int getY() {
        return this.y;
    }

    void setX(int val) {
        this.x = val;
    }

    void setY(int val) {
        this.y = val;
    }
}

Point p1 = new Point(0, 0);
p1.getX() // => 0;
p1.getY() // => 0;

// The Point3D class 'extends' Point inheriting it's behavior
class Point3D extends Point {
    private int z;

    static Point3D(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    int getZ() {
        return Z;
    }

    void setZ(int val) {
        this.z = val;
    }
}

Point3D p2 = Point3D(0, 0, 0);
p2.getX() // => 0
p2.getY() // => 0
p2.getZ() // => 0

In contrast to defining objects by class prototypal object system support a more direct method of object creation. For example in JavaScript an object is a simple list of properties. Then each object contains a special reference to another parent or prototype object that it inherits behavior from. We can mimic the Point example in JavaScript like this:

var point = {
    x : 0,
    y : 0
};

point.x // => 0
point.y // => 0

// creates a new object with 'point' as it's prototype inheriting 'point's behavior
point3D = Object.create(point);
point3D.z = 0;

point3D.x // => 0
point3D.y // => 0
point3D.z // => 0

Note, that the fundamental difference between classical and prototypal object systems is that classical objects are defined abstractly as part of a conceptual group and inherit characteristics from other classes or groups of objects. Whereas, prototypal objects are defined concretely as specific objects and inherit behavior from other specific objects.

That means that a class-based object oriented language has a dual nature, requiring the existance of at least two fundamental constucts: classes and objects. For example, in our Point example, in order to create a point object we first have to define a Point class then create a point instance and even then the point instance is never indepedant of it's class. As a result if this duality, as class-based software grows a complex class hierarchy tends to develop, and since it's generally impossible to predict all the ways that classes will need to be used in the future, this means that the class hierarchy needs to be constantly refactored in order to facilitate new features.

Prototype-based languages eliminate the need for this kind of duality, and facilitate the direct creation and manipulation of objects. Without objects being bound by class we can create more loosly bound systems of objects which helps to maintain modularity and reduce the need for refactoring.

This ability to directly define objects also adds tremendous power and simplicity to object creation and manipulation. For instance, in our previous example we can simply declare our point object with one line:

var point = { x: 0, y: 0 };

with this we have a complete working object that inherits behavior from JavaScript's Object.prototype such as the toString method. Then, when we want to extend our object's behavior we simply declare another object with point as it's prototype. Whereas, even in the most concise classical object oriented language we would have to first define a class then instantiate it before we have a manipulatable object, then to inherit we would have to define another class to extend the previously defined class.

Further, the prototype pattern is conceptually simpler. As humans, we think in terms of prototypes often. For example, in Steve Yegge's blog entry, "The Universal Design Pattern" he cites the example of a football player, say Emit Smith, who with his speed, his agility, his shear force, becomes the prototype for all new players in the NFL. Then, when a hot new running back, LT, gets picked up, they say,

"LT's got legs like Emit",

"he can plow through the line just like Emit",

"but he, runs the mile in five minutes flat!".

When they do so they're modeling a new object, LT, in terms of a prototype object, Emit Smith. In JavaScript it would look something like this:

var emit = {
    // ... properties go here
};

var lt = Object.create(emit);
// ... add other properties directly to lt

We could contrast this to classical modeling where we might define a class RunningBack which inherits from the class FootballPlayer, and LT and Emit would be instances of RunningBack. These classes might look like this in Java:

class FootballPlayer {
    private string name;
    private string team;

    static void FootballPlayer() { }

    string getName() {
        return this.name;
    }

    string getTeam() {
        return this.team;
    }

    void setName(string val) {
        this.name = val;
    }

    void setTeam(string val) {
        this.team = val;
    }
}

class RunningBack extends FootballPlayer {
    private bool offensiveTeam = true;

    bool isOffesiveTeam() {
        return this.offensiveTeam;
    }
}

RunningBack emit = new RunningBack();
RunningBack lt   = new RunningBack();

As we can see the classical model comes with considerably more conceptual overhead (to be fair the FootballPlayer class isn't one hundred percent necessary but it's there for comparison with the next example) without the fine-grained control over our class instances emit and lt that we get with JavaScript. At times this can be helpful, but very often it's just baggage, and beyond this it's quite easy to emulate classical modeling with a prototypal object system (admittedly, it's also possible to do the reverse, although, perhaps not easily). For instance we can also create an object footballPlayer, with another runningBack object that inherits from footballPlayer as it's prototype. These objects would look like this in JavaScript:

var footballPlayer = {
    name : "";
    team : "";
};

var runningBack = Object.create(footballPlayer);
runningBack.offensiveTeam = true;

we could also create another lineBacker object that also inherits from footballPlayer

var lineBacker = Object.create(footballPlayer);
lineBacker.defensiveTeam = true;

and we can add behavior to both lineBacker and runningBack by adding it to footballPlayer like this:

footballPlayer.run = function () { this.running = true };
lineBacker.run();
lineBacker.running; // => true

Notice, that here we're treating footballPlayer as a class. We can also create objects for Emit and LT:

var emit = Object.create(runningBack);
emit.superbowlRings = 3;

var lt = Object.create(emit);
lt.mileRun = '5min';

And because lt inherits from emit we can also treat emit as a class like this:

emit.height = "6ft";
lt.height // => "6ft";

To do this in a language that features static, classical objects, like Java, would require the use of the decorator pattern, requiring yet more conceptual overhead, and we still couldn't inherit directly from emit as an instance. Whereas the properties pattern used in prototype-based languages like JavaScript allow you to decorate your objects in a much more liberated way.

JavaScript Isn't Java

JavaScript as a language, and some of it's features (it's prototypal objects, for example) have been the victim of some unfortunate historical blunders, and marketing decisions. For example in his blog entry entitled "Popularity" Brendan Eich, the father of JavaScript, when talking about why a new language was needed he says "The diktat from upper engineering management was that the language must 'look like Java'. That ruled out Perl, Python, and Tcl, along with Scheme." (italics are his) So JavaScript looks like Java, and it's name looks like Java, which is naturally very confusing for anyone not familiar with either or both. What makes JavaScript look bad is that while on the surface it looks like Java, on a deeper level it's nothing like Java at all, leading to missed expectations, from Brendan Eich, "I'm not proud, but I'm happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients. The Java influences, especially y2k Date bugs but also the primitive vs. object distinction (e.g., string vs. String), were unfortunate."

Missed expectations are tough to deal with. When a person expects a static, enterprise-y language like Java but ends up with a language that has Java-like syntax but behaves more like Scheme and Self it might come as a bit of a surprise. If you like dynamic languages this would be a welcomed surprise, it you don't or they're just unfamiliar to you then programming in JavaScript might be like being force-fed spinach.

Beyond missed expectations, JavaScript does have some genuine warts: forced global variables, scoping issues, semicolon insertion, the inconsistent behavior of ==, and more. For these JavaScript programmers have developed an array of patterns and best practices to aid in the development of reliable software. Let's consider a few patterns to use and some that may be best to avoid so as to make the best use of JavaScript's prototypal object system.

JavaScript Object Patterns

While trying to make JavaScript look like Java, it's designers included constructor functions, something necessary in classical languages, but usually unnecessary overhead in a prototypal language. Let's consider the pattern in further detail. An object can be declared using a constructor function like this:

function Point(x, y) {
    this.x = x;
    this.y = y;
}

then the object is created using the new keyword, similar to Java:

var p = new Point(3, 4);
p.x // => 3
p.y // => 4

The new keyword is a bit of unfortunate syntax because it leads to the false expectation that we are creating as class instance (which we're not).

In JavaScript functions are also objects so methods can be added to the prototype of the constructor function:

Point.prototype.r = function() {
    return Math.sqrt((this.x * this.x) + (this.y * this.y));
};

along with constructor functions you can use a psudoclassical inheritance pattern like this:

function Point3D(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
}

Point3D.prototype = new Point(); // inherits from Point

Point3D.prototype.r = function() {
    return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
};

While this is certainly a perfectly valid way of defining objects in JavaScript it feels a bit clumsy, and adds unnecessary noise to your code compared to embracing the prototypal pattern and defining objects purely in this style. This is how we've been defining our objects in all of our previous examples, just to recap we define our object using an object literal:

var point = {
    x: 1,
    y: 2,
    r: function () {
        return Math.sqrt((this.x * this.x) + (this.y * this.y));
    }
};

then we inherit using Object.create:

var point3D = Object.create(point);
point3D.z = 3;
point3D.r = function() {
    return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
};

This method of object creation feels very natural in JavaScript and highlights the advantages of it's prototypal objects. One disadvantage of this pattern, though (as well the pseudoclassical pattern), is that it doesn't provide any member privacy. Sometimes this doesn't matter, but, sometimes it does, so let's consider a pattern that allows us to create objects with private members. In his book, JavaScript: The Good Parts, Douglas Crockford calls this the functional inheritance pattern:

var point = function(spec) {
    var that = {};

    that.getTimesSet = function() {
        return timesSet;
    };

    that.getX = function() {
        return spec.x;
    };

    that.setX = function(val) {
        spec.x = val;
    };

    that.getY = function() {
        return spec.y;
    };

    that.setY = function(val) {
        spec.y = val;
    };

    return that;
};

var point3D = function(spec) {
    var that = point(spec);

    that.getZ = function() {
        return spec.z;
    };

    that.setZ = function(val) {
        spec.z = val;
    };

    return that;
};

a constructor is used to generate your objects, private members are defined within and instances are created by passing a spec to the constructor:

var p = point({ x: 3, y: 4 });
p.getX();  // => 3
p.setX(5);

var p2 = point3D({ x: 1, y: 4, z: 2 });
p.getZ();  // => 2
p.setZ(3);

Conclusion

There's much more that can be discussed, on this topic: many other languages implement the prototype pattern, as we've mentioned Self, but also Lua, Io, Ioke, NewtonScript, and others. The prototype pattern is also useful when designing any system where simplicity and flexibility are desired (isn't that any system?), and can be implemented in any language, including staticlly typed languages. It should be clear at this point, though, that prototypal object oriented programming provides tremendous power and simplicity, it fulfills the goals of object oriented programming with great clarity and elegance, and it's among JavaScript's assets not it's worts.

This article was originally published in IBM Developer works (see http://ibm.co/KfuDID)

References