Home Best Practices Short Guide to Javascript Gotchas

Short Guide to Javascript Gotchas

Author

Date

Category

This is a blog post of our Code Reading Wednesdays from Codacy (http://www.codacy.com): we make code reviews easier and automatic.

Recently I have been working mostly in Javascript static code analysis and error detection. In this blog post I will to dive into Javascript scoping details, try to explain a few common errors that we, as developers, make and show how you can prevent those issues.

Javascript Scopes

Much has been written about Javascript scopes. It is a difficult concept to master and even experienced programmers sometimes make incorrect assumptions about how scopes work in Javascript. This is not a comprehensive explanation, but I will try to cover a few important details.
Function-scope vs Block-scope
Unlike other languages, Javascript has function level scope. This means that the declared variables/functions are accessible from anywhere within the function they were declared in (or from the global scope, if that is the case). This contrasts with block-scope which restricts the variable scope to the block they were declared in. Consider the following code:

var a = 3;
function foo() {
    var a = 2
    console.log(a);    
}
if(true)
{
    var a = 1;
    console.log(a)
}
foo();
console.log(a)

If you execute this, it will print 1 2 1. The if statement does not create a new scope, therefore, the variable declaration inside it belongs to the global scope and overrides the previously defined value of a. On the other hand, we can see that the function foo manipulates its own definition of a.
Hoisting
In Javascript, functions and variables are always declared at the beginning of their scope. If you didn't declare them at the beginning of the scope, the Javascript interpreter will move them there. This feature, usually referred to as hoisting, may cause unexpected results for those who are unaware of it.
Let's look at some examples:
console.log(a);
var a = 1;
Usually when you try to use an undeclared variable you get a ReferenceError, but in this case the output is undefined because a was hoisted to the beginning of the scope. Here is what was actually executed by the interpreter:
var a;
console.log(a); //undefined
a = 1;

Notice how the variable declaration is moved to the top, but the initialisation remains in the same line. Hoisting is also applied to function declarations, here is another example:
var a = 1;
function a() {
    return 2;
}
console.log(a) //1
console.log(a()) //TypeError: a is not a function

Here, the declarations are moved to the top and the assignment a = 1 overrides the function body. Note that function expressions, that usually take the form of var a = function () { }, follow the same rules as variable declarations, which means that the variable name will be hoisted but not the function body. A more detailed explanation of the differences between function declarations and function expressions can be found here.
As you can see, declaring variables and functions with the same name can cause a lot of problems. Ideally, all variables should be declared at the top of the scope to avoid confusion.
Implicit declarations
Implicitly declared variables do not throw an error in Javascript. Instead, they are added as a property to the global scope.
var a = 1;
function createGlobal(){
    b = 2;
}
createGlobal();
console.log(a); //1
console.log(b); //2 

After invoking the function createGlobal, the variable b was added to the global scope and became accessible from anywhere. Using global variables is already a bad practice; silently declaring a global variable with an implicit statement only makes it worse.
One way to prevent this from happening is to enable Javascript strict mode because it forbids variable declaration without the var identifier, among other features.
Common Javascript errors
Besides scoping problems, there are many other language gotchas that you should be careful with, specially if you are new to Javascript. Here are a few common ones:
Use '===' instead of '=='
The difference between these operators is simple:
  • === compares two values
  • == compares two values after making the necessary type conversions
A comprehensive explanation of how these type conversions are performed can be found in the ECMAScript 5.1 documentation, but I think that you will get the idea with a few examples:
1 == "1"  //true because "1" is converted to 1
1 === "1" //false
1 == true  //true because true is converted to 1
1 === true //false
undefined == null  //true - specific case detailed in the documentation
undefined === null //false
"hello" == "hello"  //true
"hello" === "hello" //true  

The == comparison is dangerous because its behaviour depends on the type of data being compared. Most of the time, you want to make an explicit comparison without any surprises so you should use ===.
Always specify radix parameter in parseInt
The second argument of the parseInt function is used to specify a radix for the number conversion. If no radix is specified, the function can return unexpected results. For example, if the string begins with a 0, the string may be interpreted as an octal number:
parseInt("032") //returns 26
parseInt("032", 10) //returns 32
Recent browsers already cast the string to a decimal number in this case, but, in order to prevent compatibility problems, the radix argument should always be used.
string.replace() only replaces the first occurrence
To replace all occurrences within a string you must specify the /g flag to the string.replace method, otherwise it will only replace the first occurrence of the pattern:
var zed = "Zed's dead, baby. Zed's dead."
zed.replace(/dead/, "alive")    //Zed's alive, baby. Zed's dead.
zed.replace(/dead/g, "alive")   //Zed's alive, baby. Zed's alive.
Missing parameters on function call
All function parameters in Javascript are optional. This means that you can always invoke a function with less parameters than it expects:
function concat(a, b) {
    return a + b
}
concat("hello"); //"helloundefined"

While sometimes intended, this is a frequent source of bugs because the function call does not throw any errors at all. The body is executed and the unspecified arguments have the value undefined. In order to prevent this, you could set a default value for each parameter or throw an error if any of them is undefined.

Conclusion

Writing bug free and maintainable code is a challenge that we face everyday. Despite being the assembly of the web, Javascript language leaves some room for ambiguity. A good code style reviewed by your team as whole is a great starting point to make sure some of these issues don’t get into production.

Next time I will try to analyze open source projects and detect some of these issues in real world products.

Share your opinion on /r/javascript

(shameless plug) Codacy already has analysis patterns that detect most of the problems mentioned in this article. For example, using implicit global variables or redeclaring variables with the same name in the same scope will automatically raise a red flag. If you have other complaints or issues that you want to automatically detect, feel free to contact me to talk about your code.

Edit: We just published an ebook: “The Ultimate Guide to Code Review” based on a survey of 680+ developers. Enjoy!

About Codacy

Codacy is used by thousands of developers to analyze billions of lines of code every day!

Getting started is easy – and free! Just use your  GitHub, Bitbucket or Google account to sign up.

GET STARTED

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Subscribe to our newsletter

To be updated with all the latest news, offers and special announcements.

Recent posts

What are Static Analysis Tools?

Static analysis tools are carried out on a software product in a non-runtime environment. This means that it is unnecessary to execute a program...

Your Static Code Analysis Guide

Errors - A word that is not so peaceful in the world of developers and software development. So, without any doubt, developers work hard...

Technical Debt In Coding

In this world, everybody has some kind of debt. And if we talk about the technical debt, it sounds like some kind of a...

Why we implemented Offline days at Codacy

Since the Coronavirus outbreak, like most people, we are facing a unique reality that is challenging us in many ways at the...

Pair programming at Codacy and why we do it

Pair programming, also known as pairing or “dynamic duo” model is not a new concept, and it was pioneered by C/C++ guru...