Hoisting in JavaScript

Hoisting is the process of lifting and declaring a variable defined with var to the top of its scope, be it functional scope (if defined in function) or global scope (if declared outside of a function). Additionally, function declarations are hoisted to the top of the current scope as well.

Let’s dive into some code.

Hoisting Variables

So now that we have our definition, what do we mean by “lifted and declared”?

console.log(a);   //undefined
var a = 5;

The above is actually executed as the following:

var a;
console.log(a);   //undefined
a = 5;

We can see the difference between declaring a variable (that is hoisted) and not declaring a variable at all.

console.log(a);   //undefined
console.log(b);   //ReferenceError: b is not defined
var a = 5;

Let’s go a bit further, what about variable hoisting within a function?

function getX() {
  if (condition) {
    var x = 5;    
    // x exists with a value of 5
    return x;
  } else {
    // x exists with a value of undefined
    return null;
  }
  // x exists with a value of undefined
}

Given that there is no block scope when using var, the variable x is hoisted to the top of the function.

function getX() {
  var x;
  if (condition) {
    x = 5;    
    // x exists with a value of 5
    return x;
  } else {
    // x exists with a value of undefined
    return null;
  }
  // x exists with a value of undefined
}

As variables as hoisted, so are function declarations.

Hoisting Functions

Function declarations are hoisted to the top of the scope chain.

console.log(add(1,2));   //3

function add(a,b) {
  return a+b;
}

Function expressions are not hoisted.

console.log(add(1,2));   //TypeError: add is not a function

var add = function(a,b) {
  return a+b;
}

Function hoisting overrides variable hoisting.

console.log(typeof foo);  //function
var foo;
function foo() {
  return 'bar';
}
console.log(typeof foo);  //function

For reference, if we did not have the function declaration, our log statement would print undefined

console.log(typeof foo);  //undefined
var foo;

Initializing our variable will override the function declaration.

console.log(typeof foo);  //function
var foo = 'baz';
function foo() {
  return 'bar';
}
console.log(typeof foo);  //string

This is because during execution, the above example is actually changed to the following:

function foo() {
  return 'bar';
}
var foo;
console.log(typeof foo);  //function
foo = 'baz';
console.log(typeof foo);  //string

Confusing, no? Shouldn’t what we write at author time (i.e. the lexical scope) be the same as what is happening during execution? Why yes, yes it should be. Enter ES6!

ES6 Block Scope and Hoisting

The good news is that block scope helps us avoid pitfalls introduced with hoisting. Let’s bring back our previous example using let and const instead of var.

Using let in this manner will trigger a ReferenceError.

console.log(a);   //ReferenceError: can't access lexical declaration 'a' before initialization
let a = 5;

And we will get the same error with const.

console.log(a);   //ReferenceError: can't access lexical declaration 'a' before initialization
const a = 5;

In this case, the errors are a good thing! We do not want to inadvertently access a variable before we have declared and initialized it. This will help us create cleaner and more maintainable programs as the JavaScript engine will execute the code in the order that we write it (i.e. author time).

Conclusion

Identifiers (i.e. variables) declared with the var keyword are created as if they are defined at the top of the scope (be it function or global). This process is called “hoisting” and applies to variables defined with var and function declarations.

While block scope with const and let allows us to avoid the pitfalls of hoisting when writing modern JavaScript, it is still important to understand this concept as you may work in a mixed code base (e.g. ES5 and ES6), are writing ES5 and below based on your requirements, or are simply curious of the many JavaScript quirks and are targeting a 100% completion rate.

Check out the further reading below and happy coding!

Additional Resources

Updated: