JavaScript, like many programming languages, has some great ways to compartmentalize and structure code to make it more maintainable, more readable, and more reliable. This post will cover two things that can help developers to organize their code: namespacing and singletons.
Namespacing
While JavaScript has no formal “namespaces,” the ability to have pretty much anything be a member of any object allows developers to emulate them somewhat effectively. You could consider the “window” to be its own namespace, containing what we often refer to as “global” variables. In this sense, everything created in the global scope is actually under the “window” namespace:
var foo = 'bar'; console.log( window.foo ); // bar window.foo = 'baz'; console.log( foo ); // baz
Developers are often discouraged from polluting the window object with variables; this creates several issues that aren’t necessarily Big Problems, but can create headaches and difficult debugging situations in the future.
var i = 10; do_something(); for ( i > 0; i--; ) { console.log( i ); } function do_something() { for( i = 0; i < 4; i++ ) { console.log( i ); } }
The output above, with commas separating for each call to “console.log”, is:
0, 1, 2, 3, 3, 2, 1, 0
Most programmers familiar with JavaScript and scoping will immediately see why the code above is problematic – the “i” variable in our function “do_something” wasn’t bound to the function’s scope (no use of the “var” keyword). When we called our function, the global “window.i” was accessed and overwritten. Working in the global scope also causes issues with keeping code organized:
function trim_space_from_form_input() {} function trim_space_from_url_input() {} function validate_input_from_form() {} function validate_input_from_url() {}
In programming, it’s not uncommon to have several functions that do similar tasks but somewhat differently from one another. In a global space, it becomes increasingly difficult to quickly find and parse the available functions. This can lead to duplicating work, confusing function names, and uncertainty for any developer who may come in to work after you. So, what’s a JavaScript namespace? Like just about everything else in JavaScript, you can use an object for a namespace:
var WDS = {}; WDS.forms = {}; WDS.url = {}; WDS.forms.trim_space = function() {}; WDS.forms.validate_input = function() {}; WDS.url.trim_space = function() {}; WDS.url.validate_input = function() {}; // Or, alternatively var WDS = { forms: { trim_space: function() {}, validate_input: function() {} }, url: { trim_space: function() {}, validate_input: function() {} } };
While those two methods don’t appear cleaner and indeed are a little more “wordy” than the original, what they do provide is cleanliness in the form of keeping relevant code separated from the rest of the environment. Instead of hunting through source files, the developer can inspect the “WDS” object and see what properties it has, and add his or her own properties to extend it.
A simple namespacing function can help you to quickly create objects that can be used to organize your code’s functionality:
/** * Accepts a string to create a "namespace" * * @param String ns The namespace to create - works recusively by separating children with dot "." * @param Object global[=window] Optional global object to create the namespace in, defaults to window * * @return Object The furthest point in the namespace */ function create_ns( ns, global ) { global = global || window; ns = ns.split( '.' ); // split the namespace into components for ( var key in ns ) { if ( ! ns.hasOwnProperty( key ) ) { continue; } key = ns[ key ]; // Grab the "text" key // Create the property "key" within the global object if ( ! global.hasOwnProperty( key ) ) { global[ key ] = {}; } global = global[ key ]; // reassign global so that we can continue to add sub-properties } return global; } /** * Creates the window.WDS object if it doesn't exist, and creates the forms property on window.WDS if that * doesn't exist */ create_ns( 'WDS.forms' ); // Now, WDS.forms is available to use however we want, for instance adding some methods WDS.forms.CreatForm = function() {}; WDS.forms.ProcessForm = function() {}; WDS.forms.POST_TYPE = 'POST'; /** * In this example, you could use "this" as the global so that the newly created object has a prepared internal structure */ function my_thing() { create_ns( 'utilities.string_stuff', this ); } var thing = new my_thing(); thing.utilities.string_stuff.Compare = function( a, b ) { return a == b; };
Singletons
Another design pattern that helps keep JavaScript clean, both in terms of tidyness and overhead, is the singleton pattern.
Often in programming, we are used to instantiating classes over and over again, but we also want to have classes that we only instatiate once and use them over and over. These are singletons, a good example of a singleton class would be a factory to generate new objects, or a utility class to deal with templating.
create_ns( 'WDS.util' ); WDS.util.TemplateManager = function() { if ( WDS.util.TemplateManager.prototype._singletonInstance ) { return WDS.util.TemplateManager.prototype._singletonInstance; } // "private" variables var templates = {}; var tpl_regex = new RegExp(); WDS.util.TemplateManager.prototype._singletonInstance = this; this.render = function ( id, variables ) { if ( ! this.get_tpl( id ) ) { throw "Could not find template " + id; } return apply_data( this.get_tpl( id ), variables ); }; this.add_tpl = function( id, template ) { templates[ id ] = template; }; this.get_tpl = function( id ) { return templates[ id ] || false; }; function apply_data( template, variables ) { for ( var key in variables ) { if ( ! variables.hasOwnProperty( key ) ) { continue; } tpl_regex.compile( '{' + key + '}', 'g' ); template = template.replace( tpl_regex, variables[ key ] ); } return template; } }; // Now, no matter how many times we contruct this object... var MyTemplateUtility = new WDS.util.TemplateManager(); MyTemplateUtility.add_tpl( 'my_template', '<div id="{div_id}">{div_content}</div>' ); var SomeOtherTemplateUtility = new WDS.util.TemplateManager(); console.log( SomeOtherTemplateUtility.get_tpl( 'my_template' ) ); // <div id="{div_id}">{div_content}</div> // we are still acccessing the same instance console.log( MyTemplateUtility === SomeOtherTemplateUtility ); // true
Using the singleton pattern, we can confidently instatiate the TemplateManager class anywhere in our code and be sure that we’re always dealing with the same instance as the first time we used it: all templates loaded to the utility will be accessible to the object no matter when or where it’s called upon. This also reduces code overhead in the browser, as we aren’t needlessly polluting the environment with multiple objects that all do the same thing.
Conclusion
While JavaScript, and any language really, can be a tangled ball o’ mess, we as developers can do our part to write more maintainable, organizable, and reusable code. Namespaces help keep our code organized into sensible chunks, while singletons create easily reusable classes that can persist their state during the life of a request.
Happy coding!