Nerdworks logo "The nerd shall inherit the earth."

Nerdworks Blogorama

Nerdspeak

On JavaScript code blocks, variable scope and variable hoisting
Technobabble
9/2/2011 10:50:07 PM

I am currently reading the book JavaScript Patterns, by Stoyan Stefanov and I came across this interesting tidbit of information regarding variable scopes in JavaScript.  Consider the following C++ code:

#include <iostream>

int main() {
  if(true) {
    int foo = 10;
  }

  std::cout<<foo;
  return 0;
}

Compiling this produces the following error:

scope.cpp(8) : error C2065: 'foo' : undeclared identifier

This is only to be expected. In C++, code blocks automatically define a variable scope, i.e., variables defined inside the code block are not visible outside of it. The same behavior can be observed in many of the other popular programming languages in use today such as C# and Java with one notable exception – JavaScript! Smile Consider the following JavaScript snippet:

function scope() {
    if(true) {
        var foo = 10;
    }
    print(foo);
}

scope();

Interestingly, this prints the value “10”! What’s going on here? The first thing one needs to understand about JavaScript is the notion of “variable hoisting”. Regardless of where you have declared a variable, the runtime treats it as though it has been declared at the start of the scope. Here’s an example:

function hoist() {
    try {
        print(undeclaredVariable === undefined);
        print("Yep, was able to refer to 'undeclaredVariable'.");
    }
    catch(e) {
        print("Could not use 'undeclaredVariable'.");
    }
    
    try {
        print(declaredLater === undefined);
        print("Yep, was able to refer to 'declaredLater'.");
    }
    catch(e) {
        print("Could not use 'declaredLater'.");
    }
    
    var declaredLater = 10;
}

hoist();

Running this snippet produces the following output:

Could not use 'undeclaredVariable'.
true
Yep, was able to refer to 'declaredLater'.

In the function “hoist” we have declared a variable named “declaredLater” right at the end of the function body.  Interestingly, while we were unable to refer to an undeclared variable (evidenced by the fact that the “print” statement inside the first “catch” block got executed) we seem to have no problems referring to “declaredLater” even before it is actually declared. This is so because the JavaScript runtime “hoists” all the local variables declared in the function to the top of the function.  It is as though the function had been defined like so:

function hoist() {
    var declaredLater;

    // stuff here
    
    declaredLater = 10;
}

hoist();

Note that though the variable itself gets declared at the top of the function, the initialization still happens only on the line where it occurred in the original function - which explains why the statement print(undeclaredVariable === undefined) ends up printing true.

Now, why did our “scope” function work the way it did? Turns out that in JavaScript curly braces do not introduce a variable scope. Only functions do. This means that variables declared inside of “if”, “for”, “while” etc. behave as though they have been declared at the top of the function. The aforementioned book therefore recommends moving all variable declarations to the top of the function. So, instead of doing the following:

function foo() {
    var a = [1, 2, 3, 4];
    if(someCondition() === true) {
        var b, c, d;
        // do something with b, c, d
    }
    for(var i = 0; i < a.length ; ++i) {
        // do something with "a"
    }
}

Do something like the following:

function foo() {
    var a = [1, 2, 3, 4], b, c, d, i, len = a.length;

    if(someCondition() === true) {
        // do something with b, c, d
    }
    for(i = 0; i < len ; ++i) {
        // do something with "a"
    }
}

This makes it clear that the scope of the variables in question is the function “foo”.

The key takeaways therefore are the following:

  • Code blocks do not determine variable scope in JavaScript. Functions are the only construct that can be used to limit scope of variables.
  • Regardless of where variables are declared, the runtime always treats them as though they have been declared at the beginning of the function in question.
  • If you need some global code to be executed, then use an immediate function (also known as a “self-executing” function) to limit the scope of the local variables you use in that code. So instead of doing the following:

    <script>
      var v1, v2;
      // some code here that uses v1 and v2
    </script>

    Use code like this:

    <script>
      (function() {
          var v1, v2;
          // some code here that uses v1 and v2
      })();
    </script>
 
Kailash 9/2/2011 11:21:27 PM
Useful and very Helpful! Thanks!
 
Ranju V 9/2/2011 11:23:02 PM
Thanks Kailash!
 
Thirumalai 10/6/2011 10:45:51 PM
Super. Thanks. However, I am a developer from the good old days of programming (1990). So by convention, I used to declare the variables at the top by default, for that matter in Javascript or in C#. This example in Javascript was a good one to know about its code blocks.
 
Ranju V 10/6/2011 10:53:21 PM
Thanks Thirumalai!
 
Ram Sagar Mourya 1/31/2012 10:15:23 AM
Thanks for sharing.
Now i understood the actual reason behind the variable scope.
 
Ranju V 1/31/2012 11:15:22 AM
Thanks Ram. Glad you found it useful.
 

Please fill this form and click on the "Submit" button to post a comment. All fields except the comment box are optional. You don't have to give me your name and email, but if you do then that might allow me to follow up with you on your comment. Also, I won't publish your email address here or anywhere else.

 
Your Name :
Your Email :
Your Comment :
   

What in your opinion do you get when you multiply the number 5 by the number 2?

Your answer will help me figure out whether you are human or a spam bot. If you're a spam bot I hope your kernel core dumps and your CPU bursts into flames.

   

Please click here to go back to the blog.

blogorama home
about this blog
email the author
where on earth am i?
subscribe to mailing list
feeds Use these links for feed syndication
rss  |  atom
by category
technobabble (54)
philosophical crud (3)
irrelevant stuff (7)
archive
july, 2011 (3)
june, 2011 (2)
may, 2011 (3)
april, 2011 (1)
march, 2011 (1)
february, 2011 (1)
february, 2010 (1)
october, 2009 (1)
september, 2009 (1)
july, 2009 (5)
march, 2009 (2)
august, 2008 (2)
march, 2008 (1)
january, 2008 (1)
september, 2007 (2)
april, 2007 (1)
february, 2007 (2)
december, 2006 (1)
october, 2006 (1)
september, 2006 (4)
august, 2006 (3)
july, 2006 (4)
june, 2006 (3)
may, 2006 (6)
april, 2006 (2)
recent entries
IE9 web cast / Chen...
Partial function ap...
Web Camps, Virtual...
On the HTML5 Indexe...
On the HTML5 Indexe...
On the HTML5 Indexe...
On the HTML5 Indexe...
On JavaScript code...
JavaScript strict m...
On JavaScript stric...
179789 hits