,

JavaScript Library Techniques, Tips and Tricks

You need only to look as far as your preferred JavaScript library to get an indication of how much JavaScript has evolved over the years. Most libraries implement some powerful features and the programming techniques that govern these libraries now closely resemble OO constructs found in server-side languages.

As you mature in your JavaScript knowledge, you may find yourself not only wanting to use but also wanting to understand the core functionality inherent to each library. Deconstructing the source, you will discover common threads running through each line of code. If you can learn to imitate and manipulate these patterns to your benefit, then you will find that you have reached an important milestone.

The following are a few tips and tricks that are gleaned from libraries in order to help you get started.

Namespacing and anonymous, self-executing functions

Namespacing in JavaScript is synonymous with namespaces, or packages, in server-side languages. Instead of mimicking the same behaviour by placing several JavaScript files in different folders, library designers will often sub-divide a single object into several smaller, meaningful objects. For instance, consider some of the namespaces in the Yahoo! YUI (called modules), such as Anim, Dom, and Element. Similar namespaces can be found in Prototype, such as Ajax, Event, and Enumerable.

Although the technique for accessing methods and properties available in each library namespace can differ drastically, the concepts are similar. Most notably is the use of anonymous, self-invoking (or self-executing) functions. Take a look at the simplest example of namespacing below.

var JSANT = {
	ajax : {
		// some code here...
	},
	events : {
		// some code here...
	},
	dom : {
		// some code here...
	}
};
JSANT.ajax.someMethod();
JSANT.dom.someProperty;

This namespace is effortless to implement, is the easiest to understand, and does not use a self-executing function. It is often referred to as object literal notation, or a nested object literal. If you never roll out your own library to other developers, and have no need for private methods and properties, then this is probably the most straightforward approach. The next example is a little more complicated.

var JSANT = function() {
	var myPrivateProperty = null;
	function myPrivateMethod() {
		// some code here...
	}
	return {
		ajax : {
			myPrivateMethod();
		},
		events : {
			// some code here...
		},
		dom : {
			// some code here...
		}
	};
}(); // the paranthesis will execute the function immediately	
JSANT.ajax.someMethod();
JSANT.dom.someProperty;

Instead of using an object literal for the global namespace, I am using a self-executing function, and then storing (or pointing to) this object with the variable named JSANT. This approach allows you the additional benefit of private methods and properties, which are only accessible from your public methods and properties. This is often referred to as the Modular Design Pattern.

The pattern is possible due to the fact that a function that self-executes, only executes once. Whatever is returned that one time, is now stored as a part of the namespace through your global variable. If you have also tried to master closures, well, this is your chance to shine. When an inner function like JSANT.ajax.someMethod() accesses a variable within the outer JSANT function (like myPrivateProperty) after it executes, then you have a closure.

The final and most complicated namespacing technique is the use of the dollar sign ($) as a substitute for your global namespace. The following code is derived from the jQuery JavaScript library, and it has several benefits. The first of which is the same as the second example — private methods and properties. The second benefit is the ability to use the dollar sign not only as a namespace but as a selector for selecting elements in the DOM. The final benefit is compatibility with other libraries that also use the dollar sign selector syntax.

(function() {
	window.JSANT = function( s ) {
		if ( this instanceof JSANT ) {
			return this.init( s );
		} else {
			return new JSANT( s );
		}
	};
	if ( window.$ != "undefined" ) {
		window.$ = window.JSANT;
	}
	JSANT.prototype = {
		init : function( s ) {
			// do something with your selector...
		}
	};
})();
$(".class");
$("#id");

If I were to walk through the previous code verbally, it would sound something like this:

  • Create a self-executing anonymous function that contains all of your code, and protects your library from conflicting with variables already defined in the document.
  • Create a variable called JSANT, point it to a function, and assign this to the window object.
  • Pass JSANT one parameter, which will end up being the syntax for your selector.
  • If an instance of the JSANT object already exists, then initialize it.
  • If an instance of the JSANT object does not exist, then create it recursively.
  • If the dollar selector is not being used by another library (making it undefined), assign it to the window object, and then point it to the JSANT object. The $ and JSANT can now be used interchangeably.
  • Define the prototype of JSANT, and all additional namespaces, methods and properties.

As best as I understand it, and have read it explained elsewhere, JSANT must be declared with the prototype. This is because the instanceof follows the prototype chain to see if your object inherits from the prototype of the constructor function that is the class you are asking instanceof. If someone from the jQuery team would like to venture a better explanation in layman’s terms, it might be more helpful.

Constructor type checking

Many developers are plagued by the fact that although objects in JavaScript have types, checking for those types can be a difficult proposition. When I first discovered constructor type checking, it was certainly an aha! moment. The previously less acceptable method for checking an object’s type, was to use the typeof keyword. Unfortunately, for custom objects and Arrays, JavaScript always returns “object” as the type. Although accurate, it is certainly not specific enough. To get more exact type definitions, use instead of the name property on the constructor for an object.

function getObjectType( obj ) {
	return obj.constructor.name;
}
	
window.onload = function() {
	alert( getObjectType( "Hello World!" ) );
	function Cat() {
		// some code here...
	}
	alert( getObjectType( new Cat() ) );
}

Variable arguments, function overloading, and the callback function

Polymorphism in JavaScript is a difficult OO concept to emulate. Unlike Java, there really is no such thing as overloading a function. You cannot define multiple functions with the same name, pass them different numbers and types of arguments, and expect the JavaScript engine to find the correct function based upon those arguments. However, there is an alternative that comes close, and that is the use of variable arguments.

A function can gain access to the arguments that were passed into it using the arguments keyword. You do not even need to define variable names to match up with the arguments, as in the first example below. However, arguments are not an array, so in order to treat it as such, you will need to call (or convert) it into a real Array object.

function myFunction() {
	var args = Array.prototype.slice.call( arguments );
	for ( i = 0; i < args.length; i++ ) {
		alert( args[ i ] );
	}
}
window.onload = function() {
	myFunction( "Hello World!", "Howdy World!", "Hey World!" );
}

If you are the only developer using the function and know this list of variable arguments will serve a single purpose, then you can reap the benefits. However, if you want to provide more flexibility, and roll the function out as a utility to other developers, then you will probably need to consider the second example.

function myFunction( message, iteration ) {
	if ( arguments.length == 2 ) {
		for ( i = 0; i < iteration; i++ ) {
			alert( message );
		}
	} else {
		alert( message );
	}
}
	
window.onload = function() {
	myFunction( "Hello World!", 3 );
}

This approach might at first glance appear less flexible, but it allows you to treat your function as if it were being overloaded. Each conditional block can share local variables and perform a different task depending on the number of arguments. This also sets some expectations for other developers.

The final example utilizes variable arguments to help process a set of key/value pairs in a hashmap and allows other developers the ability to specify an optional callback function. This is an extremely flexible, yet very defined feature that is present in several JavaScript libraries. Returning a callback function allows you to notify users after an action is complete, perform updates in the background, and it is generally a very useful tool.

function myFunction( args, callback ) {
	for ( arg in args ) {
		if ( arg != "elId" ) {
			document.getElementById( args.elId ).style[ arg ] = args[ arg ];
		}
	}
	if ( arguments.length == 2 ) {
		return new callback;
	}
}
	
window.onload = function() {
	myFunction( { elId : "obj", position : "absolute", top : "5px", left : "125px" }, function() {
		alert( "Hello World!" );
	});
}

Shorthand

The final technique that I will cover that is used in JavaScript libraries today is shorthand. One of the positive aspects of the language is that the syntax is flexible, which allows several developers to code to their comfort level in a single script. This flexibility can cause some confusion and verboseness if no conventions have been agreed upon, but it can also lead to the creative use of shorthand. The following are three academic examples that demonstrate some of this shorthand.

/*      Conditional Shorthand:
	If "a" is undefined, or is not equal to true,
	then "a" is equal to "b".   */
	
        var a, b;
	a = a || b;
	
/*      Conditional Shorthand:
	If some condition is equal to true,
	then "a" is equal to "b", or else
	"a" is equal to "c".     */
	
	var a, b, c;
	a = ( true ) ? b : c;
	
/*      Assignment Operator Shorthand:
	"d" is equal to "c", which is equal to "b",
	which is equal to "a". Therefore, "d", "c",
	and "b" are all equal to "a".    */
	
	var a, b, c, d;
	d = c = b = a;

Chainability

One topic that I did not cover in this tutorial is chainability — also known as object chaining, or DOM element chaining. This is a very powerful feature in many libraries that deserves special attention. Object chaining allows you to perform an action on an element in the DOM, return it as an object, and then perform another action on that object without having to specify the element again. You could call this a preview of things to come!

 

Originally written by Brian Reindel
and posted on 02/04/2008

Written by Rounak Kayathwal

Geek Gamer Programmer #entrepreneur Everything TECH

CEO at Alivex, Inc.

One Comment

Leave a Reply
  1. Nice article.

    For jQuery-like namespacing, the reason why the methods must be declared in the prototype is not because of the way instanceof works, as instanceof will consider anything created with the JSANT constructor to be an instance. The reason is that the method (init in the example) is needed recursively as the constructor is called.

    This is why this won’t work:

    (function() {
        window.JSANT = function( s ) {
            if ( this instanceof JSANT ) {
                // doesn't work
                // because when new JSANT(s) is called
               // init is still undefined
                return this.init( s );
            } else {
                var x = new JSANT( s );
               x.init = function (s) {
                    // method here
                }
            }
        };
        if ( window.$ != "undefined" ) {
            window.$ = window.JSANT;
        }
    })();

    One way to make it work, is to check whether the method is defined before it’s called (but this is much uglier than defining it in the prototype):

    (function() {
        window.JSANT = function( s ) {
            if ( this instanceof JSANT ) {
                // ugly
                if (this.init = "undefined") {
                  this.init = function (s) {
                    // method here
                  }
                }
                return this.init( s );
            } else {
                return new JSANT( s );
            }
        };
        if ( window.$ != "undefined" ) {
            window.$ = window.JSANT;
        }
    })();
    

Leave a Reply

Your email address will not be published. Required fields are marked *

Javascript Wooden Logo

Maintainable and Reusable JavaScript By Using Coding Conventions