Scoping & Hoisting in JavaScript

Scoping

JavaScript scoping is different to classical languages, and can take some getting used to for programmers used to languages such as C, C++, C#, Java.
Classical languages like the before mentioned have block scope.
JavaScript has function scope.

In the following example “5” will be alerted.

var foo = 1;
function bar() {
   if (!foo) {
      var foo = 5;
   }
   alert(foo);
}
bar();


In the following example “1” will be alerted.

var a = 1;
function b() {
   a = 10;
   return;
   function a() {}
}
b();
alert(a);


In the following example Firebug will show 1, 2, 2.

var x = 1;
console.log(x); // 1
if (true) {
   var x = 2;
   console.log(x); // 2
}
console.log(x); // 2


In JavaScript, blocks such as if statements, do not create new scope. Only functions create new scope.

There is a workaround though 😉
JavaScript has Closure.
If you need to create a temporary scope within a function, do the following.

function foo() {
   var x = 1;
   if (x) {
      (function () {
         var x = 2;
         // some other code
      }());
   }
// x is still 1.
}

Line 3: begins a closure
Line 6: the closure invokes itself with ()

I discuss closure in depth in a later post.

Hoisting

Terminoligy

As far as I know…
function declaration or function statement
are the same thing.
function expression or variable declaration with function assignment
are the same thing.


A function statement looks like the following:

function foo( ) {}


A function expression looks like the following:

var foo = function foo( ) {};


A function expression must not start with the word “function”.

//anonymous function expression
var a = function () {
   return 3;
};

//named function expression
var a = function bar() {
   return 3;
};

//self invoking named function expression.
(function sayHello() {
   alert('hello!');
})();

//self invoking anonymous function expression.
(function ( ) {
   var hidden_variable;
   // This function can have some impact on
   // the environment, but introduces no new
   // global variables.
}() );


In JavaScript, a name enters a scope in one of four basic ways:

  1. Language-defined: All scopes are, by default, given the names this and arguments.
  2. Formal parameters: Functions can have named formal parameters, which are scoped to the body of that function.
  3. Function declarations: These are of the form function foo() {}.
  4. Variable declarations: These take the form var foo;.

Function declarations and variable declarations are always hoisted
invisibly to the top of their containing scope by the JavaScript interpreter.
Function parameters and language-defined names are, obviously, already there. This means that code like this:

function foo() {
   bar();
   var x = 1;
}

Is actually interpreted like this:

function foo() {
   var x;
   bar();
   x = 1;
}


It turns out that it doesn’t matter whether the line that contains the declaration would ever be executed.
The following two functions are equivalent:

function foo() {
   if (false) {
      var x = 1;
   }
   return;
   var y = 1;
}
function foo() {
   var x, y;
   if (false) {
      x = 1;
   }
   return;
   y = 1;
}

The assignment portion of the declaration is not hoisted.
Only the identifier is hoisted.
This is not the case with function declarations, where the entire function body will be hoisted as well,
but remember that there are two normal ways to declare functions. Consider the following JavaScript:

function test() {
   foo(); // TypeError 'foo is not a function'
   bar(); // 'this will run!'
   var foo = function () { // function expression assigned to local variable 'foo'
      alert('this won't run!');
   }
   function bar() { // function declaration, given the name 'bar'
      alert('this will run!');
   }
}
test();

In this case, only the function declaration has its body hoisted to the top. The name ‘foo’ is hoisted, but the body is left behind, to be assigned during execution.

Including named function expressions added to the local object

//'use strict';

var container;

function Container() {

   var that = this;
   var descender = 3;
   var targetMin = 0;
   var ascender = 0;
   var targetMax = 3;    

   this.inc = function () {

      if (ascender < targetMax) {
         ascender += 1;
         console.log('ascender incremented: now equals ' + ascender);
         return that;
      } else {
         that.inc = function () {
            console.log('inc now modified to return ' + targetMax);
         };
         that.inc();
         return that;
      }
   };

   alert(dec); // Uncaught ReferenceError: dec is not defined. we're actually looking for the global objects dec property which doesn't exist.
   alert(this.dec); // Prints 'undefined' because this.dec is hoisted and declared, but of course not yet defined.
   // If this.dec was not hoisted, we would get the same 'Uncaught ReferenceError: this.dec is not defined'.

   this.dec = function () {

      if (descender > targetMin) {
         descender -= 1;
         console.log('descender decremented: now equals ' + descender);
         return that;
      } else {
         that.dec = function () {
            console.log('dec now modified to return ' + targetMin);
         };
         that.inc();
         return that;
      }
   };
}

container = new Container();
container.inc().inc().inc().inc();
console.log(container.inc);
container.dec().dec().dec().dec();
console.log(container.dec);

Out of interest, the output looks like the following:

modify routine on the fly

Name Resolution Order

The most important special case to keep in mind is name resolution order. Remember that there are four ways for names to enter a given scope. The order I listed them above is the order they are resolved in. In general, if a name has already been defined, it is never overridden by another property of the same name. This means that a function declaration takes priority over a variable declaration. This does not mean that an assignment to that name will not work, just that the declaration portion will be ignored. There are a few exceptions:

  • The built-in name arguments behaves oddly. It seems to be declared following the formal parameters, but before function declarations. This means that a formal parameter with the name arguments will take precedence over the built-in, even if it is undefined. This is a bad feature. Don’t use the name arguments as a formal parameter.
  • Trying to use the name this as an identifier anywhere will cause a Syntax Error. This is a good feature.
  • If multiple formal parameters have the same name, the one occurring latest in the list will take precedence, even if it is undefined.

Now that you understand scoping and hoisting, what does that mean for coding in JavaScript?
The most important thing is to always declare your variables with var statements.
Declare your variables at the top of the scope (as already mentioned JavaScript only has function scope). See the Variable Declarations section.
If you force yourself to do this, you will never have hoisting-related confusion.
However, doing this can make it hard to keep track of which variables have actually been declared in the current scope.
I generally like to follow these coding standards with JavaScript.

function foo(a, b, c) {
   var x = 1;
   var bar;
   var baz = 'something';
   // other non hoistable code here
}
Advertisements

Tags: , , , ,

One Response to “Scoping & Hoisting in JavaScript”

  1. James Says:

    Thank you – really nice post on hoisting – helped me a lot. This page is good too:

    http://www.programmerinterview.com/index.php/javascript/javascript-hoisting/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: