Click here to Skip to main content
13,737,584 members
Click here to Skip to main content
Add your own
alternative version

Stats

42.8K views
40 bookmarked
Posted 13 Aug 2013
Licenced CPOL

JavaScript Design Patterns

, 3 Sep 2013
Rate this:
Please Sign up or sign in to vote.
JavaScript has now emerged as the obvious language of client side web development. Right from start, browsers have introduced various features of support for accessing and modifying Document Object Model components. jQuery has abstracted most of native functionality into newer constructs and now pro

JavaScript has now emerged as the obvious language of client side web development. Right from start, browsers have introduced various features of support for accessing and modifying Document Object Model components. jQuery has abstracted most of native functionality into newer constructs and now progressive libraries like KnockOUT and BackBone are taking it a lot further.

However with native JavaScript there were certain issues, if not taken care of.

  • For variable declaration, if we forget "var", it becomes a global variable.
  • If two functions have same name in different JavaScript files, the one in the last file to be included in page, will remain and others are lost.
These can be ignored in smaller projects, but in huge applications with multiple developers working, they can create issues.
  • A variable without "var" will be in-scope of other JavaScript files also, mixing up variables in those. With lots of such variables spread across multiple files, it will be very difficult to locate the exact variable to be used for a purpose, often one variable will override all others and crash the whole state.
  • Functions for similar functionality having same name in different files (for different purpose) will clash. A code review can only identify issues within the same file, but if many such files are present, it creates an issue to find all same named functions.
Hence to solve such issues, certain design patterns have been introduced in JavaScript.

Simulated Classes

Change all global functions names and variables to tags and cover them under a single function scope. This creates a logical class; binding properties and functionalities together.

// Class wrapping
var anchorChange = {
    config : {
            colors: ["#F12", "#999"]
    },

    alterColor:  function (link, newColor) {
        link.style.backgroundColor = newColor;
    },

    init : function() {
        var self = this;  // assign reference to current object to 'self'

        var anchors = document.getElementsByTagName("a");
        var size = anchors.length;

        for (var i = 0; i < size; i++) {
            anchors[i].color = self.config.colors[i];
            anchors[i].onclick  = function () {
                self.alterColor(this, this.color); // this is bound to anchor
                return false;     // prevent navigation
            };
        }
    }
};

 

<form id="form1" runat="server">
        <div>
            <ul>
                <li><a href="www.nitinsingh.com">My Website</a></li>
                <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
            </ul>

            <!-- Now call the function, not in the head but below the controls -->
            <script type="text/javascript">
                anchorChange.init();
            </script>
        </div>
    </form>

This wrapper, although provides safety against mixing up functions and variables, it does not differentiate between public and private functions.

Module Pattern

Clearly distinguishes between the public and private items. Anything defined in a direct access is private, anything defined in 'return' block is public.

/*  Module Pattern  */
var anchorChangeMod = function () {
    // private property
    config = {
            colors: ["#F12", "#999"]
    }

    // private method, available within 'anchorChange' class not outside
    function alterColor(link, newColor) {
        link.style.backgroundColor = newColor;
    }

    return {
        // all public code inside 'return' block
        changeColor: function(link, newColor) {
            // Calls private method inside public method
            alterColor(link, newColor);
        },

        init : function() {
            var self = this;  // assign reference to current object to 'self'

            var anchors = document.getElementsByTagName("a");
            var size = anchors.length;

            for (var i = 0; i < size; i++) {
                anchors[i].color = config.colors[i];
                anchors[i].onclick = function () {
                    // this is bound to anchor..
                    anchorChangeMod.changeColor(this, this.color);
                    return false;     // prevent navigation
                }
            }
        }
    };
}();

 

<form id="form1" runat="server">
        <div>
            <div>
                <ul>
                    <li><a href="www.nitinsingh.com">My Website</a></li>
                    <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
                </ul>
            </div>
        </div>
        <script type="text/javascript">
            // No constructor, directly call object
            anchorChangeMod.init();
        </script>
    </form>

Revealing Module Pattern

In this, the entire structure is declared within the class block, then at end of class, have a 'return' block with the functions added to publicly exposed delegates.

/*  Revealing Module  */
        function anchorChangeRev() {

            // private property
            config = {
                colors: ["#F12", "#999"]
            }

            // private method, available within 'anchorChange' class not outside
            function alterColor(link, newColor) {
                link.style.backgroundColor = newColor;
            }

            var changeColor = function (link, newColor) {
                alterColor(link, newColor);
            }

            var init = function () {
                var self = this;  // assign reference to current object to 'self'
                var anchors = document.getElementsByTagName("a");
                var size = anchors.length;

                for (var i = 0; i < size; i++) {
                    anchors[i].color = config.colors[i];

                    anchors[i].onclick = function () {
                        anchorChangeRev().cc(this, this.color); // this is bound to anchor
                        return false;     // prevent navigation
                    }
                }
            }

            return {
                // delegates to actual methods are marked as public
                cc: changeColor,
                init: init
            }
        };

 

<form id="form1" runat="server">
        <div>
            <ul>
                <li><a href="www.nitinsingh.com">My Website</a></li>
                <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
            </ul>
            <script type="text/javascript">
                anchorChangeRev().init();
            </script>
        </div>
    </form>

Here first is the variable through which the function gets called, second is the function delegate. Both these functions will get public scope in the final object.

Lazy Function

If we have some functionality to be run only once (often for initialization) and other repeatedly, we apply the lazy function.

We create one class and within that declare the code to be executed only once. Then within the same scope, declare the same class with the functions to be run multiple times.

/*  Lazy loading   */
        var anchorChangeLazy = function () {

            // private property
            var config = {
                colors: ["#F12", "#999"]
            };

            var anchors = document.getElementsByTagName("a");
            var size = anchors.length;

            for (var i = 0; i < size; i++) {
                anchors[i].color = config.colors[i];

                anchors[i].onclick = function () {
                    anchorChangeLazy().changeColor(this, this.color); // this is bound to anchor
                    return false;     // prevent navigation
                }
            }

            // Redefine function to hold just the changeColor function
            // which will be invoked multiple times
            anchorChangeLazy = function () {
                return {
                    changeColor: function (link, newColor) {
                        link.style.backgroundColor = newColor;
                    }
                };
            };
        };

 

<form id="form1" runat="server">
        <div>
            <ul>
                <li><a href="www.nitinsingh.com">My Website</a></li>
                <li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
            </ul>
            <script type="text/javascript">
                anchorChangeLazy();         // just the main function, no subfunction
            </script>
        </div>
    </form>

Now whenever the class object is called, the binding in the initial code is executed only once, whereas changeColor handler (within the new class code) is made available for the usage forever.

Custom Object Constructor

We can add functions to the 'prototype' property of any object. That injects the definition into an already declared object. So the initialization function be wrapped in the constructor, and other functions added to the prototype binding.

/*  Custom object constructor  */
var anchorChangeCusCon = function() {
    this.init();
};

// 'prototype' means class definition
//  any properties can be added to this
anchorChangeCusCon.prototype.config = {
    colors: ["#F12", "#999" ]
};

anchorChangeCusCon.prototype.changeColor = function(link, newColor) {
    link.style.backgroundColor = newColor;
};

anchorChangeCusCon.prototype.init = function() {
    var self = this;

    var anchors = document.getElementsByTagName("a");
    var size = anchors.length;

    for (var i = 0; i < size; i++) {
        anchors[i].color = self.config.colors[i];

        anchors[i].onclick   = function() {
            self.changeColor(this, this.color);
            return false;
        }
    }
}


// Call of this function
var anchor = new anchorChangeCusCon();
// anchor.init();  Not needed, since this is invoked within the constructor

So we have discovered how to provide some of the OOPs principles in our own JavaScript classes for abstraction and data hiding.

Enjoy beautifying your client side code :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nitin Singh India
Technical Lead 3PillarGlobal
India India
I am a solutions lead working on .NET for about 10 years, focusing on core framework implementations in project. My development interests are in middle tier and backend components and sometimes, identifying design elements for technical solutions. Apart, I collect coins and stamps and do photography whenever I get a chance.

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 2 Pin
Kevin Mack2-Feb-15 15:21
memberKevin Mack2-Feb-15 15:21 
GeneralMy vote of 1 Pin
Slawomir8-Sep-13 23:58
memberSlawomir8-Sep-13 23:58 
GeneralMy vote of 5 Pin
Volynsky Alex4-Sep-13 2:05
memberVolynsky Alex4-Sep-13 2:05 
QuestionMy vote of 1 Pin
Amir Jalilifard24-Aug-13 22:01
memberAmir Jalilifard24-Aug-13 22:01 
QuestionNot an article... Pin
Dave Kreskowiak24-Aug-13 3:21
mvpDave Kreskowiak24-Aug-13 3:21 
GeneralMy vote of 1 Pin
Member 1017234022-Aug-13 5:52
memberMember 1017234022-Aug-13 5:52 
GeneralRe: My vote of 1 Pin
R. Giskard Reventlov3-Sep-13 10:04
memberR. Giskard Reventlov3-Sep-13 10:04 
GeneralMy vote of 2 Pin
Peter_Olson20-Aug-13 7:01
memberPeter_Olson20-Aug-13 7:01 
Questionconfig ? Pin
jsc4219-Aug-13 23:24
memberjsc4219-Aug-13 23:24 
AnswerRe: config ? Pin
Nitin Singh India20-Aug-13 5:45
memberNitin Singh India20-Aug-13 5:45 
GeneralRe: config ? Pin
Mehuge10-Nov-13 0:48
memberMehuge10-Nov-13 0:48 
QuestionGood Article, but... Pin
Dewey15-Aug-13 23:07
memberDewey15-Aug-13 23:07 
BugErrors.... Pin
Nitij15-Aug-13 7:34
memberNitij15-Aug-13 7:34 
GeneralRe: Errors.... Pin
Nitin Singh India16-Aug-13 7:16
memberNitin Singh India16-Aug-13 7:16 
SuggestionRe: Errors.... Pin
Nitij16-Aug-13 8:20
memberNitij16-Aug-13 8:20 
GeneralRe: Errors.... Pin
Nitin Singh India16-Aug-13 20:12
memberNitin Singh India16-Aug-13 20:12 
QuestionExtra call to init() in the last code sample? Pin
DenisPostu14-Aug-13 20:48
memberDenisPostu14-Aug-13 20:48 
AnswerRe: Extra call to init() in the last code sample? Pin
Nitin Singh India16-Aug-13 6:24
memberNitin Singh India16-Aug-13 6:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04-2016 | 2.8.180920.1 | Last Updated 3 Sep 2013
Article Copyright 2013 by Nitin Singh India
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid