Click here to Skip to main content
Click here to Skip to main content

JavaScript Best Practices

, 30 Apr 2013
Rate this:
Please Sign up or sign in to vote.
Reduce quirky bugs and headaches by applying these best practices.
Prize winner in Competition "Best Web Dev article of April 2013"

Introduction

The use of JavaScript has exploded over time. Now it is practically unheard of for a website not to utilize JavaScript to some extent. As a web developer who has concentrated on back-end coding in C# and front-end look and feel via HTML and CSS, my skills in JavaScript evolved over time instead of by conscious effort. While this is not uncommon, it can allow for some bad habits to be formed. This set of best practices is my way of taking a step back and addressing JavaScript as a first-class language, with both good parts and bad parts. My concentration will be on just JavaScript, regardless of where it is run. However, you will see references in here to the browser and to Visual Studio. This is simply because that is where I live, not because either are necessary for these best practices to apply. And so, without further ado, let's jump right in and see just how far down this rabbit hole goes.

Use === to Test Equality

When testing equality, a lot of languages with syntax similar to JavaScript use the double equals operator (==). However, in JavaScript you should always use triple equals (===). The difference is in how equality is determined. A triple equals operator evaluates the two items based upon their type and value. It makes no interpretations.  Let's look at a couple examples:

if(1 === '1') //Returns false
if(1 == '1') //Returns true

if(0 === '') //Returns false
if(0 == '') //Returns true

The first line would equal false because the number one does not equal the character 1. That is what we would expect. The double equals operator, on the other hand, will attempt to coerce the two values to be the same type before comparing equality. This can lead to unexpected results. In the second grouping, the result using the double equals would be true. That probably isn't what we were expecting.

Just to be clear here, the same rule applies to the inequality operator as well. Looking at our above tests, we can see that both types of inequality operators work the same way as their counterparts:

if(1 !== '1') //Returns true
if(1 != '1') //Returns false

if(0 !== '') //Returns true
if(0 != '') //Returns false

The bottom line here is that we should always use the triple equals operator (or !== for not equal) rather than the double equals. The results are far more expected and predictable. The only exception would be once you are positive you understand what is happening and you absolutely need the coercion before comparison.

Include All Necessary Semicolons

Most developers won't intentionally fail to put semicolons in the proper places. However, you need to be aware that the browser will usually put in semicolons where it thinks they are necessary. This can enable a bad habit that will come back to bite you down the road. In some instances, the compiler might assume that a semicolon is not needed, which will introduce tricky, hard-to-find bugs into your code. Avoid this by always adding the proper semicolons. A good tool to help you check your JavaScript for forgotten semicolons is JSLint.

Use JSLint

As I mentioned above, JSLint is a great tool for helping you identify common problems in your JavaScript code. You can paste your code into the website listed above, you can use a different site like JavaScriptLint.com, or you can use one of the many downloadable JSLint tools. For instance, Visual Studio has an add-in for JSLint that will allow you to check for errors at compile-time (or manually).

Whatever you choose to do, the key point here is to run a tool like JSLint on your code. It will pick up bad code that is being masked by the browser. This will make your code cleaner and it will help to prevent those pesky bugs from showing up in production.

Use a Namespace

When you first start using JavaScript, the temptation is to just declare everything and use it as needed. This places all of your functions and variables into the global scope. The problem with this, besides it being sloppy, is that it makes your code extremely vulnerable to being affected by other code. For instance, consider the following example:

var cost = 5;
//...time goes by...
console.log(cost);

Imagine your surprise when the alert pops up and says "expensive" instead of 5. When you trace it down, you might find that a different piece of JavaScript somewhere else used a variable called cost to store text about cost for a different section of your application.

The solution to this is namespacing. To create a namespace, you simply declare a variable and then attach the properties and methods you want to it. The above code would be improved to look like this:

var MyNamespace = {};
MyNamespace.cost = 5;
//...time goes by...
console.log(MyNamespace.cost);

The resulting value would be 5, as expected. Now you only have one variable directly attached to the global context. The only way you should have a problem with naming conflicts now is if another application uses the same namespace as you. This problem will be much easier to diagnose, since none of your code will work (all of your methods and properties will be wiped out).

Avoid Using Eval

The Eval function allows us to pass a string to the JavaScript compiler and have it execute as JavaScript. In simple terms, anything you pass in at runtime gets executed as if it were added at design time. Here is an example of what that might look like:

eval("alert('Hi');");

This would pop up an alert box with the message "Hi" in it. The text inside the eval could have been passed in by the user or it could have been pulled from a database or other location.

There are a couple reasons why the eval function should be avoided. First, it is significantly slower than design time code. Second, it is a security risk. When code is acquired and executed at runtime, it opens a potential threat vector for malicious programmers to exploit. Bottom line here is that this function should be avoided at all costs.

Use Decimals Cautiously

When is 0.1 + 0.2 not equal to 0.3? When you do the calculation in JavaScript. The actual value of 0.1 + 0.2 comes out to be something like 0.30000000000000004. The reason for this (nope, not a bug) is because JavaScript uses Binary Floating Point numbers. To get around this issue, you can multiply your numbers to remove the decimal portion. For instance, if you were to be adding up the cost of two items, you could multiply each price by 100 and then divide the sum by 100. Here is an example:

var hamburger = 8.20;
var fries = 2.10;
var total = hamburger + fries;
console.log(total); //Outputs 10.299999999999999

hamburger = hamburger * 100;
fries = fries * 100;
total = hamburger + fries;
total = total / 100;
console.log(total); //Outputs 10.3

Start Blocks on the Same Line

Most developers that write software in other C-family programming languages use the Allman style of formatting for block quotes. This places the opening curly brace on its own line. This pattern would look like this in JavaScript:

if(myState === 'testing') 
{
   console.log('You are in testing');
} 
else 
{
   console.log('You are in production');
}

This will work most of the time. However, JavaScript is designed in such a way that following the K&R style of formatting for blocks is a better idea. This format starts the opening curly brace on the same line as the preceding line of code. It looks like this:

if(myState === 'testing') {
   console.log('You are in testing');
} else {
   console.log('You are in production');
}

While this may only seem like a stylistic difference, there can be times when there is a impact on your code if you use the Allman style. Earlier we talked about the browser inserting semicolons where it felt they were needed. One fairly serious issue with this is on return statements. Let's look at an example:

return
{
   age: 15,
   name: Jon
}

You would assume that the object would be returned but instead the return value will be undefined. The reason for this is because the browser has inserted a semicolon after the word return, assuming that one is missing. While return is probably the most common place where you will experience this issue, it isn't the only place. Browsers will add semi-colons after a number of keywords, including var and throw.

It is because of this type of issue that is is considered best practice to always use the K&R style for blocks to ensure that your code always works as expected.

Use Explicit Blocks

There are a number of shortcuts and one-liners that can be used in lieu of their explicit counterparts. In most cases, these shortcuts actually encourage errors in the future. For instance, this is acceptable notation:

if (i > 3)
   doSomething();

The problem with this is what could happen in the future. Say, for instance, a programmer were told to reset the value of i once the doSomething() function was executed. The programmer might modify the above code like so:

if (i > 3)
   doSomething();
   i = 0;

In this instance, i will be reset to zero even if the if statement evaluates to false. The problem might not be apparent at first and this issue doesn't really jump off the page when you are reading over the code in a code review.

Instead of using the shortcut, take the time necessary to turn this into the full notation. Doing so will protect you in the future. The final notation would look like this:

if (i > 3) {
   doSomething();
}

Now when anyone goes in to add additional logic, it becomes readily apparent where to put the code and what will happen when you do.

Declare All Variables First

Most languages that conform to the C-family style will not put an item into memory until the program execution hits the line where the item is initialized.

JavaScript is not like most other languages. It utilizes function-level scoping of variables and functions. When a variable is declared, the declaration statement gets hoisted to the top of the function.  The same is true for functions. For example, this is permissible (if horrible) format:

function simpleExample(){
   i = 7;
   console.log(i);
   var i;
}

What happens behind the scenes is that the var i; line declaration gets hoisted to the top of the simpleExample function. To make matters more complicated, not only the declaration of a variable gets hoisted but the entire function declaration gets hoisted. Let's look at an example to make this clearer:

function complexExample() {
   i = 7;

   console.log(i);         //The message says 7
   console.log(testOne()); //This gives a type error saying testOne is not a function
   console.log(testTwo()); //The message says "Hi from test two"

   var testOne = function(){ return 'Hi from test one'; }
   function testTwo(){ return 'Hi from test two'; }
   var i = 2;
}

Let's rewrite this function the way JavaScript sees it once it has hoisted the variable declarations and functions:

function complexExample() {
   var testOne;
   function testTwo(){ return 'Hi from test two'; }
   var i;
   i = 7;

   console.log(i);         //The message says 7
   console.log(testOne()); //This gives a type error saying testOne is not a function
   console.log(testTwo()); //The message says "Hi from test two"

   testOne = function(){ return 'Hi from test one'; }
   i = 2;
}

See the difference? The function testOne didn't get hoisted because it was a variable declaration (the variable is named testOne and the declaration is the anonymous function). The variable i gets its declaration hoisted and the initialization actually becomes an assignment down below.

In order to minimize mistakes and reduce the chances of introducing hard to find bugs in your code, always declare your variables at the top of your function and declare your functions next, before you need to use them. This reduces the chances of a misunderstanding about what is going on in your code.

Do Not Use "With"

It is possible to shorten a long namespace using the with statement. For instance, this is technically correct syntax:

with (myNamespace.parent.child.person) {
   firstName = 'Jon';
   lastName = 'Smyth';
}

That is equivalent of typing the following:

myNamespace.parent.child.person.firstName = 'Jon';
myNamespace.parent.child.person.lastName = 'Smyth';

The problem is that there are times when this goes badly wrong. Like many of the other common pitfalls of JavaScript, this will work fine in most circumstances. The better method of handling this issue is to assign the object to a variable and then reference the variable like so:

var p = myNamespace.parent.child.person;
p.firstName = 'Jon';
p.lastName = 'Smyth';

This method works every time, which is what we want out of a coding practice.

Be Careful Using typeof

Again, the edge cases here will bite you if you aren't careful. Normally, typeof returns the string representation of the value type ('number', 'string', etc.) The problem comes in when evaluating NaN ('number'), null ('object'), and other odd cases. For example, here are a couple of comparisons that might be unexpected:

var i = 10;
i = i - 'taxi'; //Here i becomes NaN

if (typeof(i) === 'number') {
   console.log('i is a number');
} else {
   console.log('You subtracted a bad value from i');
}

The resulting alert message would be "i is a number", even though clearly it is NaN (or "Not a Number"). If you were attempting to ensure the passed in value (here it is represented by 'taxi') subtracted from i was a valid number, you would get unexpected results.

While there are times when it is necessary to try to determine the type of a particular value, be sure to understand these (and other) peculiarities about typeof that could lead to undesirable results.

Treat parseInt With Care

Just like the typeof function, the parseInt function has quirks that need to be understood before it is used. There are two major areas that lead to unexpected results. First, if the first character is a number, parseInt will return all of the number characters it finds until it hits a non-numeric character. Here is an example:

parseInt("56");    //Returns 56
parseInt("Joe");   //Returns NaN
parseInt("Joe56"); //Returns NaN
parseInt("56Joe"); //Returns 56
parseInt("21.95"); //Returns 21

Note that last example I threw in there to trip you up. The decimal point is not a valid character in an integer, so just like any other character, parseInt stops evaluating on it. Thus, we get 21 when evaluating 21.95 and no rounding is attempted.

The second pitfall is in the interpretation of the number. It used to be that a string with a leading zero was determined to be a number in octal format. Ecmascript 5 (JavaScript is an implementation of Ecmascript) removed this functionality. Now most numbers will default to base 10 (the most common numbering format). The one exception is a string that starts with "0x". This type of string will be assumed to be a hexadecimal number (base 16) and it will be converted to a base 10 number on output. To specify a number's format, thus ensuring it is properly evaluated, you can include the optional parameter called a radix. Here are some more examples to illustrate these possibilities:

parseInt("08");     //Returns 8 - used to return 0 (base 8)
parseInt("0x12");   //Returns 18 - assumes hexadecimal
parseInt("12", 16); //Returns 18, since base 16 is specified

Do Not Use Switch Fall Through

When you execute a switch statement, each case statement should be concluded by a break statement like so:

switch(i) {
   case 1:
      console.log('One');
      break;
   case 2:
      console.log('Two');
      break;
   case 3:
      console.log('Three');
      break;
   default:
      console.log('Unknown');
      break;
}

If you were to assign the value of 2 to the variable i, this switch statement would fire an alert that says "Two". The language does permit you to allow fall through by omitting the break statement(s) like so:

switch(i) {
   case 1:
      console.log('One');
      break;
   case 2:
      console.log('Two');
   case 3:
      console.log('Three');
      break;
   default:
      console.log('Unknown');
      break;
}

Now if you passed in a value of 2, you would get two alerts, the first one saying "Two" and the second one saying "Three". This can seem to be a desirable solution in certain circumstances. The problem is that this can create false expectations. If you do not see that a break statement is missing, you may add logic that gets fired accidentally. Conversely, you may notice later that a break statement is missing and you might assume this is a bug. The bottom line is that fall through should not be used intentionally in order to keep your logic clean and clear.

Avoid For...In Loops

The For...In loop works as it is intended to work, but how it works surprises people. The basic overview is that it loops through the attached, enumeration-visible members on an object. It does not simply walk down the index list like a basic for loop does. The following two examples are NOT equivalent:

// The standard for loop
for(var i = 0; i < arr.length; i++) {}

// The for...in loop
for(var i in arr) {}

In some cases, the output will act the same in the above two cases. That does not mean they work the same way. There are three major ways that for...in is different than a standard for loop. These are:

  1. It loops through all of the enumeration-visible members, which means it will pick up functions or other items attached to the object or its prototype.
  2. The order is not predictable (especially cross-browser).
  3. It is slower than a standard for loop.

If you fully understand for...in and know that it is the right choice for your specific situation, it can be a good solution. However, for the other 99% of situations, you should use a standard for loop instead. It will be quicker, easier to understand, and less likely to cause weird bugs that are hard to diagnose.

Use Var to Declare Variables

When declaring a variable, always use the var keyword unless you are specifically attaching the variable to an object. Failure to do so attaches your new variable to the global scope (window if you are in a browser). Here is an example to illustrate how this works:

function carDemo() {
   var carMake = 'Dodge';
   carModel = 'Charger';
}

console.log(carMake);  //Undefined, since carMake is defined inside the testing function scope
console.log(carModel); //Charger, since this variable has been implicitly attached to window

The declaration of the carModel variable is the equivalent of saying window.carModel = 'Charger';. This clogs up the global scope and endangers your other JavaScript code blocks, since you might inadvertently change the value of a variable somewhere else.

Avoid Reserved / Special Words

JavaScript is rather flexible with what it allows you to do. This isn't always a good thing. For instance, when you create a function, you can specify that one of the parameters be named arguments. This will overwrite the arguments object that every function is given by inheritance. This is an example of a special word that isn't truly reserved. Here is an example of how it would work:

// This function correctly accesses the inherited
// arguments parameter
function CorrectWay() {
   for(var i = 0; i < arguments.length; i++) {
      console.log(arguments[i]);
   }
}

// You should never name a parameter after 
// a reserved or special word like "arguments"
function WrongWay(arguments) {
   for(var i = 0; i < arguments.length; i++) {
      console.log(arguments[i]);
   }
}

// Outputs 'hello' and 'hi'
CorrectWay('hello', 'hi');

// Outputs 'h', 'e', 'l', 'l', and 'o'
WrongWay('hello', 'hi');

There are also reserved words that will cause you issues when you attempt to run your application. A complete listing of these words can be found at the Mozilla Developer Network. While there are work-arounds to use some of these words, avoid doing so if at all possible. Instead, use key words that won't conflict with current or potential future reserved or special words.

Be Consistent 

When I originally developed my list of best practices, this one was so obvious I overlooked it. Fortunately Daniele Rota Nodari pointed it out to me. Keeping a consistent standard is important to writing easily understandable code. Matching the coding style of the application you are working in should become second nature, even if that means changing your personal style for the duration of the project. When you get the opportunity to start a project, make sure that you have already established a personal coding style that you can apply in a consistent manner.

While being inconsistent with how you write your code won’t necessarily add bugs into your application, it does make your code harder to read and understand. The harder code is to read and understand, the more likely it is that someone will make a mistake. A good post on JavaScript coding styles and consistency in applying them can be found here: http://flaviocopes.com/javascript-coding-style/. The bottom line here is that you need to write consistent code. If you bring snippets into your application, format them to match your existing style. If you are working in someone else’s application, match your code to the existing style. 

Read Good Code

As with any software development language, reading the code of other developers will help you improve your own skills. Find a popular open source library and peruse the code. Figure out what they are doing and then identify why they chose to do things that way. If you can't figure it out, ask someone. Push yourself to learn new ways to attach common problems.

Conclusion

JavaScript is not C#. It is not Java or Java-lite. It is its own language. If you treat it as such, you will have a much easier time navigating its particular peculiarities. As you may have noticed, the common theme throughout many of these best practices is that there are hidden pitfalls that can be avoided by simply modifying how you approach certain problems. Little formatting and layout techniques can make a big difference in the success of your project.

Before I finish, I wanted to point out that there are a number of best practices related to JavaScript in HTML that I have not mentioned. For instance, a simple one is to include your JavaScript files at the bottom of your body tag. This omission is intentional. While I've made passing references to both the browser and Visual Studio, the above tips are purely about JavaScript. I will be writing a separate article that covers JavaScript in the browser.

Thus concludes my attempt at compiling a list of JavaScript best practices. Please be sure to let me know if you have others that you think should be added to the list.

License

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

About the Author

Tim Corey
Software Developer (Senior) Epicross
United States United States
I am currently a Lead Technical Consultant for a consulting company called Epicross. My primary skills are in .NET, SQL, JavaScript, and other web technologies although I have worked with PowerShell, C, and Java as well.
 
In my previous positions, I have worked as a lead developer and IT Director. As such, I have been able to develop software on a number of different types of systems and I have learned how to correctly oversee the overall direction of technology for an organization. I've developed applications for everything from machine automation to complete ERP systems.
 
My current position is mainly focused making our clients more efficient and effective. I use custom software (desktop, mobile, and web) to help facilitate this goal. When I'm not working for the company, I'm usually developing applications to fill the needs of the organizations I volunteer for.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 5 PinmemberSanjay K. Gupta5-May-14 18:55 
Questionbeautiful article Pinmembertawfik.kh2-Feb-14 10:57 
QuestionMy vote of 5 PinmemberBill_Hallahan15-Nov-13 9:33 
GeneralMy vote of 5 PinmemberAlan_caozy12-Aug-13 12:25 
QuestionMy vote of 5 PinmemberPriyanka77779-Jul-13 0:25 
GeneralMy vote of 5 PinmemberRohan Leuva12-Jun-13 19:28 
GeneralMy vote of 5 PinmemberJenka198030-May-13 5:01 
GeneralMy vote of 5 PinmemberLoveJenny27-May-13 15:32 
GeneralRe: My vote of 5 PinprofessionalTim Corey28-May-13 2:23 
GeneralMy vote of 5 PinprofessionalSunasara Imdadhusen16-May-13 20:04 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 30 Apr 2013
Article Copyright 2013 by Tim Corey
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid