Embrace prototypal object-oriented programming

JavaScript, the lowest-level programming interface to the web, is everywhere. As the web becomes increasingly a part of daily life, JavaScript becomes more and more relevant. JavaScript is an often-misunderstood language, considered by some a toy language or a "baby Java™ language." One of its more maligned features is its prototypal object system. Though it's undeniable that JavaScript has warts, the prototypal object system isn't one of them. In this article, learn about the tremendous power, simplicity, and elegance of JavaScript prototypal object-oriented programming.

Delon Newman, Freelance Developer, Freelance

Photo of Delon NewmanDelon Newman has been programming for fun since 1997. He started with C and C++ and later moved on to HTML, Perl, and JavaScript. He has worked in IT since 1999 as a help desk support technician, graphic designer, web designer, systems administrator, programmer, analyst, and software engineer. While working for his own company and as a consultant, Delon has explored many languages and environments including Ruby, Python, Java, C#, PHP, Smalltalk, Lisp, Haskell, Erlang, Scala, and Clojure. You can reach Delon at delon.newman@gmail.com.



05 June 2012

Also available in Chinese Russian Japanese Vietnamese

A world of objects

As you go about the day—driving to work, sitting at a desk performing a task, eating a meal, walking through a park—you can usually manipulate and interact with your world without having to know the detailed physical laws that govern it. You can treat the various systems you deal with each day as units, or objects. You take for granted their complexity and focus instead on your interactions with them.

History

Simula, a language for modeling, is generally considered the first object-oriented language. Then came Smalltalk, C++, the Java language, and C#. At that point, in most OO languages objects were defined by class. Later, developers of the Self programming language, a Smalltalk-like system, created an alternative and lighter-weight method of defining objects called prototype-based or prototypal OO programming.

Eventually, JavaScript was developed with a prototype-based object system. JavaScript's popularity has brought prototype-based objects to the mainstream. Though many developers find this distasteful, upon closer inspection prototype-based systems have many advantages.

Object-oriented (OO) programming, which is an attempt to create software systems that work similarly, is a powerful and wildly popular modeling tool for software development. It's popular because it reflects the way we view the world: as a collection of objects that can interact with each other and be manipulated in various ways. The power of OO programming lies in its two core principles:

Encapsulation
Lets developers conceal the inner workings of their data structures and reveal reliable programming interfaces that can be used to create modular, adaptable software. Think of it as information hiding.
Inheritance
Amplifies the power of encapsulation by allowing objects to inherit the encapsulated behavior of other objects. Think of it as information sharing.

These principles are well known to most developers because every mainstream programming language supports OO programming (and, in many cases, enforces it). Although all OO languages support the two core principles, in one form or another, over the years there have been at least two fundamentally different ways of defining objects.

In this article, learn about the benefits of prototypal OO programming and JavaScript object patterns.


Prototypo-what? Classes and prototypes

A class provides an abstract definition of objects that defines shared data structures and methods for an entire class or collection of objects. Each object is defined as an instance of its 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 its child Point3D for defining two-dimensional and three-dimensional points, respectively. Listing 1 shows how the classes would look in Java code.

Listing 1. Java Point class
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 its 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 systems support a more direct method of object creation. For example, in JavaScript an object is a simple list of properties. Each object contains a special reference to another parent, or prototype, object from which it inherits behavior. You can mimic the Point example in JavaScript, as shown in Listing 2.

Listing 2. JavaScript Point class
var point = {
    x : 0,
    y : 0
};

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

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

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

There's a fundamental difference between classical and prototypal object systems. Classical objects are defined abstractly as part of a conceptual group and inherit characteristics from other classes, or groups, of objects. In contrast, prototypal objects are defined concretely as specific objects and inherit behavior from other specific objects.

Thus, a class-based OO language has a dual nature that requires at least two fundamental constructs: classes and objects. As a result of this duality, as class-based software grows, complex class hierarchies tend to develop. It's generally impossible to predict all the ways classes will need to be used in the future, so the class hierarchy needs to be constantly refactored to facilitate changes.

Prototype-based languages eliminate the need for the above-mentioned duality and facilitate the direct creation and manipulation of objects. Without objects being bound by class, more loosely bound systems of objects can be created that help to maintain modularity and reduce the need for refactoring.

Being able to directly define objects also adds tremendous power and simplicity to object creation and manipulation. For instance, in Listing 2, you can simply declare your point object with one line: var point = { x: 0, y: 0 };. With this one line, you have a complete working object that inherits behavior from JavaScript's Object.prototype, such as the toString method. To extend your object's behavior, you simply declare another object with point as its prototype. In contrast, even with the most concise classical OO language, you would have to first define a class, then instantiate it before you have a manipulatable object. To inherit, you would have to define another class to extend the first class.

The prototype pattern is conceptually simpler. As humans, we often think in terms of prototypes. For example, in Steve Yegge's blog entry "The Universal Design Pattern" (see Resources), he cites the example of an American football player—say, Emmitt Smith—who with his speed, agility, and shear force becomes the prototype for all new players in the National Football League (NFL). Then, when an exceptional new running back, LT, gets picked up, commentators say:

"LT's got legs like Emmitt."
"He can plow through the line just like Emmitt."
"But he runs the mile in five minutes flat!"

The commentators are modeling a new object—LT—in terms of a prototype object, Emmitt Smith. In JavaScript, such a model would look something like Listing 3.

Listing 3. JavaScript model
var emmitt = {
    // ... properties go here
};

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

You could contrast the example to classical modeling, where you might define a class RunningBack that inherits from the class FootballPlayer. LT and emmitt would be instances of RunningBack. These classes might look like Listing 4 in Java code.

Listing 4. Three Java classes
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 emmitt = new RunningBack();
RunningBack lt   = new RunningBack();

The classical model comes with considerably more conceptual overhead but without the fine-grained control over class instances emmitt and lt that you get with the prototype model. (To be fair, the FootballPlayer class isn't 100% necessary; it's there for comparison with the next example.) At times, this overhead can be helpful, but often it's just baggage.

It's quite easy to emulate classical modeling with a prototypal object system. (Admittedly, it's also possible to do the reverse, though perhaps not easily.) For instance, you can create an object footballPlayer with another runningBack object that inherits from footballPlayer as its prototype. In JavaScript these objects would look like Listing 5.

Listing 5. JavaScript modeling
var footballPlayer = {
    name : "";
    team : "";
};

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

You could also create another lineBacker object that inherits from footballPlayer, as shown in Listing 6.

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

As shown in Listing 7, you can add behavior to both the lineBacker and runningBack objects by adding to the footballPlayer object.

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

In this example, you're treating footballPlayer as a class. You can also create objects for Emmitt and LT, as shown in Listing 8.

Listing 8. Creating objects
var emmitt = Object.create(runningBack);
emmitt.superbowlRings = 3;

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

Because the lt object inherits from the emmitt object, you can even treat the emmitt object as a class, as shown in Listing 9.

Listing 9. Inheritance and classes
emmitt.height = "6ft";
lt.height // => "6ft";

If you were to try the examples above in a language that features static, classical objects (like Java code), you would have to use the decorator pattern, requiring yet more conceptual overhead, and you still couldn't inherit directly from the emmitt object as an instance. In contrast, the properties pattern used in prototype-based languages like JavaScript let you decorate your objects in a much more liberated way.


JavaScript isn't the Java language

JavaScript and some of its features, such as prototypal objects, have been the victim of unfortunate historical blunders and marketing decisions. For example, Brendan Eich (the father of JavaScript) discussed in a blog entry why a new language was needed: "The diktat from upper engineering management was that the language must 'look like Java.' That ruled out Perl, Python, and Tcl, along with Scheme." So, JavaScript looks like Java code, and its name is linked to the Java lanuage, which is confusing for anyone not familiar with either or both. Though JavaScript looks like the Java language on the surface, on a deeper level it is nothing like Java—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.

Unmet expectations are tough to deal with. When you expect a static, enterprise-y language like the Java language but end up with a language that has Java code-like syntax but behaves more like Scheme and Self you're justifiably surprised. If you like dynamic languages, this would be a welcomed surprise; if you don't, or they're just unfamiliar to you, then programming in JavaScript might be unpleasant.

JavaScript also has some genuine warts: forced global variables, scoping issues, semicolon insertion, the inconsistent behavior of ==, and more. For these issues, JavaScript programmers have developed an array of patterns and best practices to aid the development of reliable software. The next section discusses a few patterns to use, and some to avoid, to make the best use of JavaScript's prototypal object system.


JavaScript object patterns

While trying to make JavaScript look like Java code, its designers included constructor functions, which are necessary in classical languages but usually unnecessary overhead in a prototypal language. Consider the pattern below, where an object can be declared using a constructor function as shown in Listing 10.

Listing 10. Declaring an object
function Point(x, y) {
    this.x = x;
    this.y = y;
}

You can then create the object using the new keyword, similar to Java code, as shown in Listing 11.

Listing 11. Creating the object
var p = new Point(3, 4);
p.x // => 3
p.y // => 4

In JavaScript, functions are also objects, so methods can be added to the prototype of the constructor function, as shown in Listing 12.

Listing 12. Adding a method
Point.prototype.r = function() {
    return Math.sqrt((this.x * this.x) + (this.y * this.y));
};

Along with constructor functions, you can use a pseudoclassical inheritance pattern, as shown in Listing 13.

Listing 13. Pseudoclassical inheritance pattern
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));
};

Although this is certainly a valid way of defining objects in JavaScript (and at times it may be the best way), it feels a bit clumsy. It adds unnecessary noise to your code compared to embracing the prototypal pattern and defining objects purely in this style. To recap, you define your object using an object literal, as shown in Listing 14.

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

As shown in Listing 15, you then inherit using Object.create.

Listing 15. 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 natural in JavaScript and highlights the advantages of its prototypal objects. One disadvantage of both the prototypal and pseudoclassical patterns, however, is that they don't provide any member privacy. Sometimes privacy doesn't matter and sometimes it does. Listing 16 shows a pattern that lets you create objects with private members. In his book, JavaScript: The Good Parts, Douglas Crockford calls this the functional inheritance pattern.

Listing 16. 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, as shown in Listing 17.

Listing 17. Creating instances
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

This article just scratched the surface of prototypal OO programming. Many other languages, such as Self, Lua, Io, and REBOL implement the prototype pattern. The prototype pattern can be implemented in any language, including statically typed languages. It is also useful when designing any system where simplicity and flexibility are desired.

Prototypal OO programming provides tremendous power and simplicity and fulfills the goals of OO programming with great clarity and elegance. It is among JavaScript's assets, not its warts.

Resources

Learn

Get products and technologies

  • Self programming language: Download Self, which includes a programming language, a collection of objects defined in the Self language, and a programming environment built in Self for writing Self programs.
  • Innovate your next development project with IBM trial software.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=819828
ArticleTitle=Embrace prototypal object-oriented programming
publish-date=06052012