The Prototype Pattern - Techniques, Strategies and Patterns for Structuring JavaScript Code

Using the JavaScript Prototype Pattern

This is the 2nd post in a series on techniques, strategies and patterns for writing JavaScript code. In my previous post I introduced what I call “function spaghetti code” and explained some of the problems it introduces. I also talked about the impact of global variables and how closures add a much needed solution. In this post I’ll introduce the Prototype Pattern and show how it relies on built-in functionality in the JavaScript language.

I’ve worked through some of the patterns I’m covering in this series with my good friend John Papa over Skype and we’ve talked about pros and cons of different approaches. For example, he initially recommended defining multiple variables using a single var based on JSLint suggestions (that was covered in the previous post) and I agreed that it was a better approach than using var for every variable. It’s interesting how code evolves with feedback from multiple people. John and I have definitely come to enjoy working with a few different patterns and although the Prototype Pattern isn’t in my favorites list, I’ve always felt that it’s good to understand and know the different options out there. That’s why I decided to include it in this series.

The Prototype Pattern can be broken out into two main sections including a constructor section and a prototype section. Prototyping allows functions and properties to be associated with objects. However, instead of each object instance getting a copy of all functions/properties each time an object is created, only one set of functions/properties exists across all objects resulting in less memory consumption. In other words, functions and properties are defined once per prototype rather than once per object.

As a quick review, I showed the following code in a previous post. The code simply lists all functions directly with no encapsulation and defines several global variables. While the code works fine this way, I’ll examine how we can restructure it to follow the Prototype Pattern.

window.onload = function () {
    eqCtl = document.getElementById('eq');
    currNumberCtl = document.getElementById('currNumber');
};

var eqCtl,
    currNumberCtl,
    operator,
    operatorSet = false,
    equalsPressed = false,
    lastNumber = null;

function add(x,y) {
    return x + y;
}

function subtract(x, y) {
    return x - y;
}

function multiply(x, y) {
    return x * y;
}

function divide(x, y) {
    if (y == 0) {
        alert("Can't divide by 0");
        return 0;
    }
    return x / y;
}
     
function setVal(val) {
    currNumberCtl.innerHTML = val;
}
        
function setEquation(val) {
    eqCtl.innerHTML = val;
}
        
function clearNumbers() {
    lastNumber = null;
    equalsPressed = operatorSet = false;
    setVal('0');
    setEquation('');
}

function setOperator(newOperator) {
    if (newOperator == '=') {
        equalsPressed = true;
        calculate();
        setEquation('');
        return;
    }
            
    //Handle case where = was pressed
    //followed by an operator (+, -, *, /)
    if (!equalsPressed) calculate();
    equalsPressed = false;
    operator = newOperator;
    operatorSet = true;
    lastNumber = parseFloat(currNumberCtl.innerHTML);
    var eqText = (eqCtl.innerHTML == '') ? 
        lastNumber + ' ' + operator + ' ' : 
        eqCtl.innerHTML + ' ' + operator + ' ';
    setEquation(eqText);
}

function numberClick(e) {
    var button = (e.target) ? e.target : e.srcElement;
    if (operatorSet == true || currNumberCtl.innerHTML == '0') {
        setVal('');
        operatorSet = false;            
    }
    setVal(currNumberCtl.innerHTML + button.innerHTML);
    setEquation(eqCtl.innerHTML + button.innerHTML);
}

function calculate() {
    if (!operator || lastNumber == null) return;
    var currNumber = parseFloat(currNumberCtl.innerHTML),
        newVal = 0;
    //eval() would've made this a whole lot simpler
    //but didn't want to use it in favor of a more
    //"robust" set of methods to demo patterns
    switch (operator) {
        case '+':
            newVal = add(lastNumber, currNumber);
            break;
        case '-':
            newVal = subtract(lastNumber, currNumber);
            break;
        case '*':
            newVal = multiply(lastNumber, currNumber);
            break;
        case '/':
            newVal = divide(lastNumber, currNumber);
            break;
    }
    setVal(newVal);
    lastNumber = newVal;
}

 

To start using the Prototype Pattern you need to first create a constructor as shown next. The constructor can accept one or more parameters and define any variables that the object needs. Note that the variables are scoped to the object rather than to the global scope.


var Calculator = function (tb, eq) {
    this.eqCtl = document.getElementById(eq);
    this.currNumberCtl = document.getElementById(tb);
    this.operator = null;
    this.operatorSet = false;
    this.equalsPressed = false;
    this.lastNumber = null;
};


Once the constructor is defined a prototype can be created using the prototype keyword. Although you can create a prototype for each function, it’s more convenient (and less typing) to take advantage of JSON-style syntax where the function name represents the JSON property name and the value represents the function. An example of defining two functions in a prototype is shown next. Notice that each function is separated with a comma just as you would separate properties defined in a normal JSON object. It's officially called a JavaScript object literal, but if you're familiar with JSON it uses the same standard syntax (thanks to Craig Stuntz for clarifying that):

Calculator.prototype = {

    add: function (x, y) {
        return x + y;
    },

    subtract: function (x, y) {
        return x - y;
    }
}


The original function-based calculator code shown earlier can be refactored to follow the Prototype Pattern as shown shown next. A prototype is created for the Calculator object and functions/properties are defined within the prototype using JavaScript object literal syntax.

Calculator.prototype = {

    add: function (x, y) {
        return x + y;
    },

    subtract: function (x, y) {
        return x - y;
    },

    multiply: function (x, y) {
        return x * y;
    },

    divide: function (x, y) {
        if (y == 0) {
            alert("Can't divide by 0");
        }
        return x / y;
    },
            
    setVal: function(val) {
        this.currNumberCtl.innerHTML = val;
    },
        
    setEquation: function (val) {
        this.eqCtl.innerHTML = val;
    },

    clearNumbers: function () {
        this.lastNumber = null;
        this.equalsPressed = this.operatorSet = false;
        this.setVal('0');
        this.setEquation('');
    },

    setOperator: function (newOperator) {
        if (newOperator == '=') {
            this.equalsPressed = true;
            this.calculate();
            this.setEquation('');
            return;
        }

        //Handle case where = was pressed
        //followed by an operator (+, -, *, /)
        if (!this.equalsPressed) this.calculate();
        this.equalsPressed = false;
        this.operator = newOperator;
        this.operatorSet = true;
        this.lastNumber = parseFloat(this.currNumberCtl.innerHTML);
        var eqText = (this.eqCtl.innerHTML == '') ?
            this.lastNumber + ' ' + this.operator + ' ' :
            this.eqCtl.innerHTML + ' ' + this.operator + ' ';
        this.setEquation(eqText);
    },

    numberClick: function () {
        var button = (event.target) ? event.target : event.srcElement;
        if (this.operatorSet == true || this.currNumberCtl.innerHTML == '0') {
            this.setVal('');
            this.operatorSet = false;
        }
        this.setVal(this.currNumberCtl.innerHTML + button.innerHTML);
        this.setEquation(this.eqCtl.innerHTML + button.innerHTML);
    },

    calculate: function () {
        if (!this.operator || this.lastNumber == null) return;
        var displayedNumber = parseFloat(this.currNumberCtl.innerHTML)
            newVal = 0;
        //eval() would've made this a whole lot simpler
        //but didn't want to use it in favor of a more
        //"robust" set of methods to demo patterns
        switch (this.operator) {
            case '+':
                newVal = this.add(this.lastNumber, displayedNumber);
                break;
            case '-':
                newVal = this.subtract(this.lastNumber, displayedNumber);
                break;
            case '*':
                newVal = this.multiply(this.lastNumber, displayedNumber);
                break;
            case '/':
                newVal = this.divide(this.lastNumber, displayedNumber);
                break;
        }
        this.setVal(newVal);
        this.lastNumber = newVal;
    }
};


To use the Calculator object, create a new instance and pass the names of the HTML container objects into the constructor (the container objects identify the IDs of controls used to display calculations – see the HTML code below):


var calc = null;
window.onload = function () {
    calc = new Calculator('currNumber', 'eq');
};


The HTML used to render the calculator can reference the calc object created when the page loads. The following code demonstrates how this can be done to handle events as different div elements are clicked. I normally prefer to wire-up events to event handlers using jQuery in “real-world” applications to keep the HTML clean but wanted to focus on JavaScript patterns and avoid introducing additional libraries for this post. I’m not a big fan of defining onclick and other event handlers directly on HTML elements but let it slide for this post.

<div class="Calculator">
    <div class="CalculatorHead">
        <div id="eq" class="Equation"></div>
        <div id="currNumber" class="CurrentNumber">0</div>
    </div>
    <div>
        <div class="Button" onclick="calc.numberClick(event);">7</div>
        <div class="Button" onclick="calc.numberClick(event);">8</div>
        <div class="Button" onclick="calc.numberClick(event);">9</div>   
        <div class="Button" onclick="calc.setOperator('/');">/</div>        
        <div class="Button rowspan2" onclick="calc.clearNumbers();">C</div>
    </div>
    <div>
        <div class="Button" onclick="calc.numberClick(event);">4</div>
        <div class="Button" onclick="calc.numberClick(event);">5</div>
        <div class="Button" onclick="calc.numberClick(event);">6</div> 
        <div class="Button" onclick="calc.setOperator('*');">*</div>           
    </div>
    <div>
        <div class="Button" onclick="calc.numberClick(event);">1</div>
        <div class="Button" onclick="calc.numberClick(event);">2</div>
        <div class="Button" onclick="calc.numberClick(event);">3</div>
        <div class="Button" onclick="calc.setOperator('-')">-</div>            
        <div class="Button rowspan2" onclick="calc.setOperator('=');">=</div>
    </div>
    <div>
        <div class="Button colspan2" onclick="calc.numberClick(event);">0</div>
        <div class="Button" onclick="calc.numberClick();">.</div>
        <div class="Button" onclick="calc.setOperator('+');">+</div>            
    </div>
</div>

 

The Prototype Pattern provides a nice way to structure JavaScript code but there are several other roads you can travel if desired. In this next post I’ll talk about the Revealing Module Pattern and explain how it can be used.

Demos of all the patterns covered in this series can be downloaded below.

Download Code



Pluralsight Course - Structuring JavaScript Code in HTML5 Applications

If you're interested in additional information about structuring JavaScript code check out my Pluralsight course. Here's a sample from the course covering closures.

Demo - Working with Closures in JavaScript




Published Monday, August 01, 2011 3:02 PM by dwahlin

Comments

# Techniques, Strategies and Patterns for Structuring JavaScript Code - Dan Wahlin&#39;s WebLog

Pingback from  Techniques, Strategies and Patterns for Structuring JavaScript Code - Dan Wahlin&#39;s WebLog

# re: Techniques, Strategies and Patterns for Structuring JavaScript Code–The Prototype Pattern

Monday, August 01, 2011 11:04 PM by Jay Turpin

Thanks for the article Dan! Have you ever read The Universal Design pattern by Steve Yegge (steve-yegge.blogspot.com/.../universal-design-pattern.html)? I thought he gave a great description of the prototype pattern.

# re: Techniques, Strategies and Patterns for Structuring JavaScript Code–The Prototype Pattern

Monday, August 01, 2011 11:10 PM by dwahlin

Jay: Haven't heard of that one but will check it out...thanks for the link.

# Techniques, Strategies and Patterns for Structuring JavaScript Code???The Prototype Pattern &#8211; Dan Wahlin&#8217;s WebLog &laquo; Jimmy2Saints

Pingback from  Techniques, Strategies and Patterns for Structuring JavaScript Code???The Prototype Pattern &#8211; Dan Wahlin&#8217;s WebLog &laquo; Jimmy2Saints

# Dew Drop &ndash; August 2, 2011 | Alvin Ashcraft&#039;s Morning Dew

Pingback from  Dew Drop &ndash; August 2, 2011 | Alvin Ashcraft&#039;s Morning Dew

# Techniques, Strategies and Patterns for Structuring JavaScript &#8230;

Pingback from  Techniques, Strategies and Patterns for Structuring JavaScript &#8230;

# re: Techniques, Strategies and Patterns for Structuring JavaScript Code–The Prototype Pattern

Tuesday, August 02, 2011 9:28 AM by Craig Stuntz

Minor nitpick: The syntax you use isn't JSON, it's a JavaScript object literal. JSON is a subset of that which doesn't support functions, etc.

# re: Techniques, Strategies and Patterns for Structuring JavaScript Code–The Prototype Pattern

Tuesday, August 02, 2011 10:36 AM by dwahlin

Craig: Great point...thanks. You're absolutely right. I'll update it to be more specific. :-)

# Techniques, Strategies and Patterns for... | ASP.NET, .NET and Javascript | Syngu

Pingback from  Techniques, Strategies and Patterns for... | ASP.NET, .NET and Javascript | Syngu

# Techniques, Strategies and Patterns for Structuring JavaScript Code–Revealing Module Pattern

Sunday, August 07, 2011 12:40 PM by Dan Wahlin's WebLog

Using the JavaScript Revealing Module Pattern This is the 3rd post in a series on techniques, strategies

# Techniques, Strategies and Patterns for Structuring JavaScript Code

Tuesday, November 01, 2011 1:25 AM by Dan Wahlin's WebLog

JavaScript has come a long way since the mid-90s when I first started working with it in Netscape 3 and

# Techniques, Strategies and Patterns for Structuring JavaScript Code

Pingback from  Techniques, Strategies and Patterns for Structuring JavaScript Code

# javascript Module pattern &laquo; Sharepoint&#8230;

Saturday, December 17, 2011 2:46 AM by javascript Module pattern « Sharepoint…

Pingback from  javascript Module pattern &laquo; Sharepoint&#8230;

# Using the JavaScript Prototype Property to Structure and Extend Code

Monday, December 19, 2011 2:26 PM by Dan Wahlin's WebLog

There are several different patterns that can be used in JavaScript to structure code and make it more

# Wow, I actually couldn&rsquo;t write JavaScript, what I wrote smelled&hellip;

Tuesday, January 10, 2012 4:52 PM by Community Blogs

Alright, I’m not really a JavaScript developer, but do they even exists? I think there probably are a

# Working with the JavaScript ???this??? Keyword :: Learning

Wednesday, January 25, 2012 4:15 PM by Working with the JavaScript ???this??? Keyword :: Learning

Pingback from  Working with the JavaScript ???this??? Keyword :: Learning