27 May 2008

My JavaScript "rules"

In my day to day life, I see a lot of JavaScript code. I see a lot of code written by others and a lot of code which I have written in the past. As I look back over my own code and my coding style over the last 2 years or so, it is easy to see how far I have come and how much better my code is now.

None of this is meant to suggest that it cannot be improved. Nor am I saying that it is even that good! But, I thought it might be useful to talk about some of the ideas I have employed. These are my JavaScript "rules". I try to apply them in everything that I do:

1. Be unobtrusive
This is an absolutely imperative and key rule. Unobtrusive JavaScript code is easier to read, easier to write and is encapsulated within a JavaScript layer in your application. Writing unobtrusive code means having no JavaScript in the markup of your documents. Make use of browser events (onLoad, DOMContentLoaded etc) to apply the JavaScript enhancements to your pages. Use ID's and if necessary classes to pick DOM nodes from the document to work with.

2. Name your spaces
Namespaces make for much friendlier JavaScript. If nothing else, a namespace will stop you polluting the global scope of the document. I always have one root namespace for an application and then other namespaces off that to define the functionally different regions of your application. Always group similar functionality. If for example you have a bunch of objects which are used for handling forms and form validation, group them all under a common namespace. Creating a namespace is as easy as creating an object. Here is an example of creating a root namespace and 2 sub-namespaces:

var Nortools = {};

Nortools.Login = {};

Nortools.Forms = {};



3. Cry over spilt milk
I always encapsulate functionality within relevant objects and namespaces. Dont let that functionality spill out of the object or the namespace. Use private members in objects to hide the secrets of an object and strengthen your code. This will prevent your application from being changed or interfered with in any way by other JavaScripts on the page. I use self executing functions to create completely closed and hidden objects, neatly encapsulating my code. It is even possible to have private members on the prototype of a constructor using this technique:

var myConstructor = function() {};

myConstructor.prototype = new (function() {

    var myPrivateMember = "some value";

    this.getPrivateMemeber = function() {

        return myPrivateMember;

    }

});


This code may look odd, but the prototype of myConstructor is now a self creating singleton object which cannot be forced to give up its secrets. I use this technique for creating all singleton objects - its encapsulated!

4. Separate
I always separate the various types of code in my applications strictly. If I am dealing with data, I create a data layer to manage all that logic with as few public methods as possible. If I have business rules, the same applies. All presentation logic should also be self contained and independent of the data and the business rules which translate that data into objects which can then be displayed.

5. I object
Use objects and use them extensively. I never store any value within global scope and with everything in a well organised object model, you always know where things are. Use the singleton pattern above (or the many others which are just as good) to create singleton objects - an object that only has one instance.

6. JSON
JSON is extremely useful and powerful. I always use JSON for storing and transferring data in my applications. I also make extensive use of object notation for providing options to my objects. Passing in a JSON object to a constructor can be much easier than handling optional parameters. This is one method for handling optional parameters:

var myConstructor = function(param1, param2, param3) {

    param1 = param1 || "defaultValue";

    param2 = param2 || "defaultValue";

    param3 = param3 || "defaultValue";

};

var myInstance = new myConstructor( "value", null, "value" );


With this method, on creating an instance, I have to know which parameter is which and what order they are in. Its also slightly clumsy looking.

My preferred method is:

var myConstructor = function(options) {

    var param1 = options.param1 || "defaultValue";

    var param2 = options.param2 || "defaultValue";

    var param3 = options.param3 || "defaultValue";

};

var myInstance = new myConstructor( {

    param2: "value",

    param3: "value"

} );


Much neater and I find it much easier to understand and read.

7. Be a detective
I always try to code defensively. I dont want errors to even be possible in my code. To this end, I always want to detect whether the operation I am about to perform is possible. Sometimes this means checking my own objects for a feature, sometimes a built in object. This is much more effective than user agent sniffing for browser features. Not only do I not have to know what each specific browser supports, but I also do not have to keep changing my code each time there is a new version of a browser. It makes my code future proof!

if (document.getElementById) { //check the browser supports getElementById

    var myElement = document.getElementById("myElement");

    if (myElement) { //check an element was found

        //perform some action

    }

}



8. Lets go native
Always use native functionality when its there. Its the fastest method. If you want to have a getElementsByClassName function in your application, check if the browser already has it built in and use that method if it is there. Replacing it will only make your application slower.

9. Delegation's what you need
Events are very important when building web applications. But adding event handlers to lots of nodes can be bad for performance and lead to management overheads. If for instance you have a list of items and clicking on an item in the list performs an action, consider adding the click event to the parent of the list items and using a delegation pattern to handle the event. For a more detailed explanation see this excellent article.

10. Get organised
I always try to organise my code well. It is vital during a build phase of a project to be well organised. I only ever put one object or constructor in a JavaScript file and I name the file accordingly. If the full path (taking into account namespaces etc) to my object is MyApplication.ApplicationSection.ObjectName, then I will name the file MyApplication.ApplicationSection.ObjectName.js. In a large application, my folder structure will represent the namespace hierarchy as well.

Choose a coding style for your application and stick to it. I have my own coding style with naming conventions etc, and I always stick to it. It makes the code more consistent and easier to read and understand.

Comment code wherever it is needed to help understand why the code does what it does. Dont comment code when the comments will not add to the understanding of the code.

11. Dont panic
Writing JavaScript is hard. Writing web applications is harder still. Dont panic. Help is at hand. Use a library.

4 comments:

Adam Silver said...

Excellent article, would be very nice to a see a "norton javascript patterns" article describing the different patterns you use in general other than the singleton pattern you have shown in this article.

Also, do you have a nice example when you have used MVC before?

Anonymous said...

Good to see these 'codified' James, they have certainly helped me on recent projects! Anything to aid maintenance and clarity. I would argue that it is this aspect of JS (aka code organisation/hierarchy/inheritance) that is much more difficult to master than the procedural code blocks themselves. How about a rule for Function.call() and Function.apply()?

Anonymous said...

Hi James,

Great post - this is up by my monitor now as my JS cheat sheet. One query though on point 10 - Isn't this in contradiction to the general advice not to incur too many HTTP requests when creating web pages, as advocated by the people at Yahoo who work on Yslow? (see here for more info).

Do you think this is anything to worry about? If you were to avoid having a JS file per class how would you organise them all within a single file?

Cheers,

Billy

James Norton said...

Hi Billy,

Thanks for your comments. When coding I would always have one object per file. Never more.

You are commenting on delivering the code to an end user and for that, I would bundle the code into just a few files or one file and minimise it first. If possible, I would do this automatically on the server, if not I would do it manually before the product goes into testing.