Introduction
Javascript hoisting explained
best practices

Javascript hoisting explained

Often I see problems arise when people try to tackle Javascript. Taking a certain behavior for granted can get them frustrated. Javascript has little differences but they can cause unexpected results if not taken into account. One of those differences is the  way it handles scope.

What is javascript hoisting?

In Javascript, you can have multiple var-statements in a function. All of these statements act as if they were declared at the top of the function. Hoisting is the act of moving the declarations to the top of the function.

Hoisting versus block-scope

In most programming languages there’s something called block-scope. Basically what it means is that every variable declared within a block is only visible within that block. The following code snippet in c# shows this behavior:

// C# void mymthod() { // code for (int i = 0; i < 10; i++) { int myvar = 5; } // more code Console.WriteLine(myvar); // this will raise a compiler error // myvar is not available here }

In the snippet above, the variable myvar is only available inside the for-loop. That’s because c# has block-scope (just as VB.NET, Java and most other languages).

If we look at Javascript however, there are only two scopes (in ES5): global and function scope. So if we write an equivalent method in Javascript the result is quite different:

function mymethod() { for (var i = 0; i < 10; i++) { var myvar = 5; } alert(myvar); // Will alert 5 } mymethod();

Try out the fiddle

Because Javascript “hoists” the declaration of myvar to the top of the function this will actually alert ‘5’. The above code is functionally equivalent to this, which explains the behavior:

function mymethod() { var myvar; for (var i = 0; i < 10; i++) { myvar = 5; } alert(myvar); // Will alert 5 } mymethod();

Unexpected behavior due to hoisting

As said, if you expect your variables to be block-scoped, you will get unexpected behavior and unexplainable errors. Let’s look at a few examples:

Variable hoisting

var myvar = "global variable"; function mymethod(){ alert(myvar); // expected "global variable", result: undefined var myvar = "local variable"; alert(myvar); // local variable } mymethod();

Try out the fiddle

Normally you would expect the first alert to display the value of the global variable since you haven’t overwritten it yet. However, since the declaration is actually hoisted to the top it overwrites it at the start of the function.
The following code is functionally equivalent:

var myvar = "global variable"; function mymethod(){ var myvar; // this is the same as var myvar = undefined; alert(myvar); // undefined myvar = "local variable"; alert(myvar); // local variable } mymethod();

Function hoisting

****When we are declaring functions inside a function there are two patterns available: function expressions and function declarations:

function something(){ // function declaration function mymethod(){} // function expression var mysecondmethod = function() {}; mymethod(); mysecondmethod(); }

As you can see, both implementations are valid and you can call them using the same syntax. This might lead you to believe that they are in fact equivalent. However, there is a small difference when it comes to hoisting: function declarations get hoisted completely (name AND implementation) whereas function expressions get only the variable declaration hoisted and not the implementation. Let’s look at an example:

function mymethod(){ alert("global mymethod"); } function mysecondmethod(){ alert("global mysecondmethod"); } function hoisting(){ alert(typeof mymethod); alert(typeof mysecondmethod); mymethod(); // local mymethod mysecondmethod(); // TypeError: undefined is not a function // mymethod AND the implementation get hoisted function mymethod(){ alert("local mymethod"); } // Only the variable mysecondmethod get's hoisted var mysecondmethod = function() { alert("local mysecondmethod"); }; } hoisting();

Try the fiddle

In this example you can see that, as with normal variables, just the presence of mymethod and mysecondmethod inside the function moves them to the top. However, mymethod (a function declaration) gets hoisted completely and therefore is available everywhere inside the function. On the other hand, mysecondmethod (a function expression) has only the var declaration hoisted, which prevents the global method from being seen but is not usable as a function until it’s assigned.

Best practices

From the examples, it’s obvious that the hoisting behavior Javascript exhibits can lead to confusing results and make it difficult to read code. Therefore you should follow some best practices.
What you really want when you read code, is that it clearly states what it does. Since you know it’s moving the variable declarations to the top, you should do this explicitly, so it’s obvious that they’re there. Consider the following examples (the first one is the “wrong” one, the second example show a better version):

function badhoistingpractices(){ var myvar = getFromWhere(); if (myvar == 5){ var mysecondvar = 20; somefunction(mysecondvar); } } function betterhoistingpractices(){ var myvar, mysecondvar; myvar = getFromWhere(); if (myvar == 5){ mysecondvar = 20; somefunction(mysecondvar); } }

Sidenote

For completeness sake, I need to mention that behind the scenes things are actually implemented a little bit different. The ECMAScript standard does not define hoisting as “the declaration gets moved to the top of the function”. Handling code happens in two steps: the first one is parsing and entering the context and the second one is runtime code execution. In the first step variables, function declarations and formal parameters are created. In the second stage function expressions and unqualified identifiers (undeclared variables) are created. However for practical purposes we can adopt the concept of hoisting.

Conclusion

With this post I tried to demystify some of the strange behavior you sometimes may experience. I hope by reading this and applying the concept you can avoid some frustration in the future. Following the best practices will get you a long way, and remember: it’s Javascript, it IS different.

Kenneth Truyers
View Comments
Next Post

Javascript Namespaces and Modules

Previous Post

Must-have NuGet packages