Using the JavaScript Prototype Property to Structure and Extend Code

There are several different patterns that can be used in JavaScript to structure code and make it more re-useable, more maintainable, and less subject to naming collisions. Patterns like the Revealing Module Pattern, Prototype Pattern, Revealing Prototype Pattern, and others can be used to structure code and avoid what I call “function spaghetti code”. One of my favorite features offered by both the Prototype and the Revealing Prototype patterns is the extensibility they provide. They’re quite flexible especially compared to the Module or Revealing Module patterns out there.

Because both patterns rely on JavaScript prototyping, functions are shared across objects instances in memory and its straightforward for users of objects following these patterns to extend or override existing functionality. If you didn’t get a chance to read my previous posts on the two prototype patterns here are some basic examples of the patterns in action:

 

Prototype Pattern Example

 

var Calculator = function (eq) {
    //state goes here     
    this.eqCtl = document.getElementById(eq);
};

Calculator.prototype = { 
    add: function (x, y) {
        this.eqCtl.innerHTML = x + y;
    },
    subtract: function (x, y) {
        this.eqCtl.innerHTML = x - y;
    } 
};


Revealing Prototype Pattern Example

 

var Calculator = function (eq) {
    //state goes here
    this.eqCtl = document.getElementById(eq);
};

Calculator.prototype = function () {
    //private members
    var add = function (x, y) {
        this.eqCtl.innerHTML = x + y;
    },
    subtract = function (x, y) {
        this.eqCtl.innerHTML = x - y; 
    };

    //public members
    return {
        add: add,
        subtract: subtract
    };
} ();

 

Looking through the two examples you can see that both patterns rely on the prototype functionality available in JavaScript. The Revealing Prototype Pattern has the added functionality of being able to define public and private members within the Calculator.

Sidebar: Some people love the public/private feature and some people think all JavaScript code should be accessible to callers (which I’ll admit can simplify debugging in some scenarios). It really depends on your background and what you want. I personally like having the Revealing Prototype Pattern’s public/private member functionality available (mainly because of my background in Java and C#) since if you’re working with editors that support Intellisense or code help you only see the functions or variables that you should call as you’re typing. It’s all personal preference though and if you’d prefer to add an underscore in front of a variable or function to mark it as private then I say go for it – to each their own.

What if you want to extend one of the Calculator objects shown above or override existing functions? When using the Prototype or Revealing Prototype patterns this is possible since they both rely on prototyping. If you’re new to prototyping here’s a quick introduction to using the prototype property and different ways it can be used in your JavaScript code.

Getting Started with JavaScript Prototyping

Prototyping allows objects to inherit, override, and extend functionality provided by other objects in a similar manner as inheritance, overriding, abstraction, and related technologies do in C#, Java, and other languages. Every object you create in JavaScript has a prototype property by default that can be accessed. To better understand prototyping, take a look at the example below. Rather than adding methods directly into the BaseCalculator constructor definition as with patterns like the Revealing Module Pattern, this example relies on separate prototype definitions to define two functions.

 

var BaseCalculator = function () {
//Define a variable unique to each instance of BaseCalculator
this.decimalDigits = 2; }; //Extend BaseCalculator using prototype BaseCalculator.prototype.add = function (x, y) { return x + y; };
BaseCalculator.prototype.subtract = function (x, y) { return x - y; };

 

This code defines a BaseCalculator object with a variable named decimalDigits in the constructor. The code then extends the BaseCalculator object using the prototype property. Two functions are added including add(x,y) and subtract(x,y). This type of definition can be simplified as shown above with the Prototype Pattern by using an object literal to define the prototype functions:

 

var BaseCalculator = function() {
    //state goes here
    this.decimalDigits = 2;
};

BaseCalculator.prototype = {
    //private members
    add: function(x, y) {
        return x + y;
    },
    subtract: function(x, y) {
        return x - y;
    }
};


Once BaseCalculator is defined you can inherit from it by doing the following:

 

var Calculator = function () {
//Define a variable unique to each instance of Calculator this.tax = 5; }; Calculator.prototype = new BaseCalculator();

 

Note that Calculator is defined with a constructor that includes a tax variable that’s unique to each object instance. The Calculator’s prototype points to a new instance of BaseCalculator() allowing Calculator to inherit the add() and subtract() functions automatically. These functions are shared between both types and not duplicated in memory as instances are created which is a nice feature provided by the prototype property. An example of creating a new Calculator object instance is shown next:

 

var calc = new Calculator();
alert(calc.add(1, 1));
//variable defined in the BaseCalculator parent object is accessible from the derived Calculator object’s constructor alert(calc.decimalDigits);

 

In the previous code, BaseCalculator’s decimalDigits variable is accessible to Calculator since a new instance of BaseCalculator was supplied to the Calculator’s prototype. If you want to disable access to parent type variables defined in the constructor you can assign BaseCalculator’s prototype to Calculator’s prototype as shown next as opposed to assigning a new BaseCalculator instance:

 

var Calculator = function () {
    this.tax= 5;
};

Calculator.prototype = BaseCalculator.prototype;


Because the BaseCalcuator’s prototype is assigned directly to Calculator’s prototype the decimalDigits variable defined in BaseCalculator will no longer be accessible if you go through a Calculator object instance. The tax variable defined in Calculator would be accessible of course. For example, the following code will throw a JavaScript error when the code tries to access decimalDigits. This is due to BaseCalculator’s constructor no longer being assigned to the Calculator prototype.

 

var calc = new Calculator();
alert(calc.add(1, 1));
alert(calc.decimalDigits);


Overriding with Prototype

If you’re using either the Prototype Pattern or Revealing Prototype Pattern to structure code in JavaScript (or any other object that relies on prototyping) you can take advantage of the prototype property to override existing functionality provided by a type. This can be useful in scenarios where a library built by a 3rd party is being used and you want to extend or override existing functionality without having to modify the library’s source code. Or, you may write code that you want other developers on your team to be able to enhance/override. An example of overriding the add() function provided in Calculator is shown next:

//Override Calculator’s add() function 
Calculator.prototype.add = function (x, y) { return x + y + this.tax; };
var calc = new Calculator(); alert(calc.add(1, 1));


This code overrides the add() function provided by BaseCalculator and modifies it to add x, y, and an instance variable named myData together. The override applies to all Calculator object instances created after the override.

There’s of course more that you can do with JavaScript prototyping so I recommend reading through this post for additional details and more advanced examples of using the prototype property.


Interested in learning more about structuring JavaScript code?

 

If you're interested in additional information about structuring JavaScript code check out my latest Pluralsight course. Here's a sample from the course covering closures and how they can be created and used.

Working with Closures in JavaScript


comments powered by Disqus

10 Comments

Comments have been disabled for this content.