JavaScript Hoisting Explained
Written by Mike James   
Wednesday, 23 January 2013
Article Index
JavaScript Hoisting Explained
Subtle Considerations

Recap

  • Hoisting makes the program behave as if all declarations were moved to the top of the function or main program that contains them.
  • Declarations in JavaScript are function declarations and variable declarations.

This is the basic idea and it is very simple but there are some subtle points we need to consider.

Implicit Variable Declarations

JavaScript doesn't insist that you actually write the var to declare a variable - it might be simpler if it did and in later versions with strict turned on it does.

It is enough to assign a value to a variable to declare it.

That is if you write

myVariable=10;

and this is the first time myVariable has appeared then the variable is declared and set to 10.

However the implicit declaration of the variable is not hoisted.

If you think of

myVariable=10;

as equivalent to

var myVariable;
myVariable=10;

then you might be surprised that the declaration isn't hoisted.

The trick, is of course, that implicit declaration isn't equivalent to explicit declaration. If you write

myVariable=10;

then myVariable is created as a global variable - even if the instruction is within a function - whereas:

var myVariable;

always creates a variable in the current scope be it global or local to a function.

So to be hoisted a variable declaration has to be explicit and make use of the var keyword.

If you don't use the var keyword then the variable is global and isn't hoisted even in the main program.

Initialization

As well as declaring a variable you can also initialize it in the same instruction.

For example:

var myVariable=10;

both declares myVariable and initializes it to 10.

By the hoisting rules this declaration should be treated as if it was at the start of the current scope - and it is.

The complication is that the initialization is left exactly where it is - you can't alter the order of executable instructions in a program.

That is

var myVariable=10;

is treated as a shorthand for

var myVariable;
myVariable=10;

and the declaration var myVariable is moved to the top of the current scope but the initialization is left where it is.

This is good news because without this rule things could become very complicated very quickly.

For example what about:

var myVariable=myFunction();

Moving the entire instruction to the top of the scope could alter the result returned by myFunction - imagine it is a timer say.

Assignment is a runtime operation and needs to be executed exactly where the programmer wrote it.

So the rules for variable declarations are:

  • explicit declarations are hoisted to the top of the current scope
  • implicit declarations are not hoisted
  • declarations with assignment hoists the declaration but not the assignment.

Anonymous functions

Now we reach the promised complication in defining functions that stops forward referencing work.

As well as named functions you can also create anonymous functions which can either be executed at once or assigned to a variable.

Now you should be ahead of me at the mention of an assignment.

As an assignment is an executable statement it shouldn't be moved and hence it shouldn't be hoisted - and there is no sensible way the function declaration can be hoisted without moving the assignment as well.

That is: anonymous functions should not be hoisted.

Consider the following example:

myFunction();
var myFunction= function(){ };

You really should be expecting what happens in this case.

The variable declaration myFunction is hoisted so when we try to call myFuncton the variable is declared but its value is undefined because the assignment isn't hoisted.

If you include an alert(myFunction) at the start of the program you will see that it is indeed undefined

So now you can see why:

myFunction();
function myFunction(){ };

works perfectly, the forward reference is allowed courtesy of hoisting, but

myFunction();
var myFunction= function(){ };

doesn't work because without hoisting you can't forward reference the function.

This is the reason why you can use a named function before it is defined but an anonymous function has to be defined before it can be used.

An anonymous function is treated differently by JavaScript - it is more a function expression than a function declaration.

Overriding Variables

You can see why the hoisting of function declaration is important - it allows forward referencing.

The real question is why bother hoisting variable declarations. After all forward referencing variable is not a problem in JavaScript because using a variable implicitly creates it i.e. implicitly declares it.

The important point is that an implicit declaration creates a global variable but an explicit declaration creates a variable in the current scope - and this makes a big difference.

For example if you create a local variable with the same name as a global variable then it hides the global in the local scope.

For example

var myGlobal=10;
myFunction();

function myFunction(){
 alert(myGlobal);
};

This works as you would expect and the alert displays 10 as the value of the global variable myGlobal. Now make a small change to the program to get:

var myGlobal=10;
myFunction();

function myFunction(){
 alert(myGlobal);
 var myGlobal=100;
};

This declares a local variable within the function which has the same name as the global. If you don't know about hoisting then you probably expect the program two display 10 as before. However the declaration is hoisted and hence myGlobal, the local variable, exists from the start of the function and hides the global variable. The value in the local variable is undefined and so this is what the alert displays.

Hoisting converts the program into:

var myGlobal=10;
myFunction();

function myFunction(){
 var myGlobal;
 alert(myGlobal);
 myGlobal=100;
};

The rule is that explicit variable declarations behave as if they are at the top of the current scope and therefore hide any variables in the containing scopes right from the start of the current scope.

Best Practice

Hoisting was introduced to allow you to forward reference functions and variables.

However, the best advice for avoiding difficulties with hoisting is to actually do the job manually. Always declare variables at the start of the main program or function - initializing if necessary and where necessary.

As to functions?

If you are using function declarations then define them in an order that is logical for the understanding of the program.

If you are using anonymous functions then you have to take account of where they are used and where they are defined - there seems to be no way of simplifying this and you just have to take into account that you can't forward reference an anonymous function.

 

  • Hoisting moves all declarations to the start of their enclosing scope.
  • Declarations in JavaScript are function declarations and variable declarations.
  • Function declarations are always hoisted and hence can always be referenced before they are defined.
  • An anonymous function is assigned to a variable and is never hoisted and hence cannot be referenced before they are defined.
  • explicit variable declarations are hoisted to the top of the current scope
  • implicit variable declarations are not hoisted
  • explicit declarations with assignment hoists the declaration but not the assignment.

If you have any thoughts on how to organize declarations in JavaScript use the comments to contribute.

 

Related Articles

The Undefined Defined Variable

 

To be informed about new articles on I Programmer, subscribe to the RSS feed, follow us on Google+, Twitter, Linkedin or Facebook, install the I Programmer Toolbar or sign up for our weekly newsletter.

 

espbook

 

Comments




or email your comment to: comments@i-programmer.info

 

Banner


JavaScript Canvas - Fetch API

Working with lower-level data is very much part of graphics. This extract from Ian Elliot's book on JavaScript Graphics looks at how to use typed arrays to access graphic data.



JavaScript Jems - The Inheritance Tax

JavaScript should not be judged as if it was a poor version of the other popular languages - it isn't a Java or a C++ clone. It does things its own way.  In particular, it doesn't do inheritance  [ ... ]


Other Articles

 



Last Updated ( Wednesday, 23 January 2013 )