{ Hoisting. }

Objectives

By the end of this chapter, you should be able to:

  • Explain where a variable name gets defined in your code
  • Describe how hoisting affects function expressions vs function declarations
  • Solve a tricky problem with hoisting

Hoisting

From MDN:

Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code.

When we write code like this:

instructor // this will NOT throw an error!
var instructor = "Elie";

What is happening under the hood is that all the variables you declare in your code are created in memory before your code gets run. Therefore in the following example, instructor will be created in memory and will be undefined until the assignment statement.

var instructor;
instructor; // this will NOT throw an error!
instructor = "Elie";

Hoisting And Inside of Functions

The same idea applies in functions. When a variable is created in a function using the var keyword, the variable is actually defined at the top of its scope. In the following example, displayInstructor function creates a new scope and var instructor is a variable defined within that scope. Therefore, javascript creates the variable in memory at the top of the function. That means the instructor will be undefined when it is returned.

function displayInstructor(){
    return instructor;
    var instructor = "Elie";
}

Hoisting In Function Declarations vs Function Expressions

So how does this work with function declarations vs function expressions?

Function declarations are fully defined before the code is run. So in the following example, we can call the sayHi function above the lines that define the sayHi function:

sayHi("Matt"); // "Hello Matt"

function sayHi(name){
    return "Hello " + name;
}

However, function expressions act differently. Since a function expression assigns an anonymous function to a variable, hoisting applies to that variable name as well. In the following example, there is an error because we are trying to invoke a function called sayHi but at that point in the code sayHi is not equal to a function. In fact, sayHi exists in memory but it is undefined.

sayHi("Matt"); // Throws an error!

var sayHi = function(name){
    return "Hello " + name;
}

The above example is equivalent to the following code. First the sayHi variable gets created at the top and is set to undefined. Then we try to invoke a function using the sayHi variable but since it is undefined, we get an erorr. If we wanted to fix the issue, we would have to move the function expression above the invocation.

var sayHi;
sayHi("Matt"); // Throws an error because undefined is not a function (we are using the type of undefined and trying to invoke it - that's why we get a TypeError)

sayHi = function(name){
    return "Hello " + name;
}

You can read more about hoisting here.

A Tricky Hoisting Problem

Imagine you have written the following code:

var myName = "Tim";

function returnMyName() {
    console.log(myName);
    var myName = "Matt";
}

returnMyName();

When returnMyName is invoked, what will be logged to the console? You may guess that myName is not defined yet in the function, so "Tim" will be logged. Or maybe you might think that the myName variable inside of the returnMyName function will be hoisted to the top of the function, so what you actually get logged in "Matt". These are both good guesses, but actually they are both wrong.

What is actually happening is that the myName variable from the returnMyName function is getting logged. It is hoisted to the top of the function and created as soon as the function executes. Since nothing has been assigned to myName until later in the program, myName starts out undefined. Not until the line var myName = "Matt"; is the myName variable defined. Therefore, the program will log undefined to the console.

Exercises

  • What does the following code output? Why?

    var firstName = 'Elie';
    
    function displayFirstName(){
        var firstName = 'Tim';
        return firstName;
    }
    
    displayFirstName(); // ?
    
  • What does the following code output? Why?

    function displayFirstName(){
        var firstName = 'Tim';
        return firstName;
    }
    
    displayFirstName();
    
    console.log(firstName) // ?
    
  • What does the following code output? Why?

    console.log(firstName) // ?
    var firstName = "Elie"
    
  • What does the following code output? Why?

    console.log(firstName) // ?
    firstName = 'Elie'
    
  • What does the following code output? Why?

    function sayHi(){
        return 'Hi ' + firstName;
        var firstName = 'Elie'
    }
    
    sayHi(); // ?
    
  • What does the following code output? Why?

    function sayHi(){
        return 'Hi ' + firstName; 
        firstName = 'Elie'
    }
    
    sayHi(); // ?
    
  • What does the following code output? Why?

    sayHi() // ?
    
    function sayHi(){
        return 'Hi!';
    }
    
  • What does the following code output? Why?

    sayHi() // ?
    
    var sayHi = function(){
        return 'Hi!';
    }
    

Solutions

  • What does the following code output? Why?

    var firstName = 'Elie';
    
    function displayFirstName(){
        var firstName = 'Tim';
        return firstName;
    }
    
    displayFirstName(); // 'Tim' is returned since the name variable defined in the function scope is return. JavaScript always starts from in (the closest function) and works its way out (to outer functions and eventually the global scope)
    
  • What does the following code output? Why?

    function displayFirstName(){
        var firstName = 'Tim';
        return firstName;
    }
    
    displayFirstName();
    
    console.log(firstName) // ReferenceError - firstName is not defined. We get an error because we are trying to access a variable defined in a function outside of its scope. 
    
  • What does the following code output? Why?

    console.log(firstName) // ?
    var firstName = "Elie"
    
    // undefined is output because the var firstName; is hoisted to the top of the code so it actually looks like this under the hood
    
    var firstName;
    console.log(firstName);
    firstName = "Elie"
    
  • What does the following code output? Why?

    console.log(firstName) // ?
    firstName = 'Elie'
    
    // since variables that do not use the var keyword are NOT hoisted, this will return a ReferenceError - firstName is not defined.
    
  • What does the following code output? Why?

    function sayHi(){
        return 'Hi ' + firstName;
        var firstName = 'Elie'
    }
    
    sayHi(); // "Hi undefined", since the firstName variable uses the var keyword, it is hoisted to the top and it's value is initialized to undefined. It looks a bit like this:
    
    function sayHi(){
        var firstName; // the value of firstName is now undefined
        return 'Hi ' + firstName;
        firstName = 'Elie' // the value of firstName has been re-assigned to 'Elie'
    }
    
  • What does the following code output? Why?

    function sayHi(){
        return 'Hi ' + firstName; 
        firstName = 'Elie'
    }
    
    sayHi(); // ReferenceError - firstName is not defined. Since we are not using the var keyword, the firstName variable does not get hoisted and JavaScript has no reference to the firstName variable.
    
  • What does the following code output? Why?

    sayHi(); // ?
    
    function sayHi(){
        return 'Hi!';
    }
    
    // since function declarations are always hoisted, this will simple return "Hi!"
    
  • What does the following code output? Why?

    sayHi(); // ?
    
    var sayHi = function(){
        return 'Hi!';
    }
    
    // TypeError: sayHi is not a function. Since we are using the var keyword, the declaration of the variable get's hoisted, but not its definition so the code looks like this:
    
    var sayHi; // the value of sayHi is undefined
    
    sayHi();
    
    sayHi = function(){
        return 'Hi!';
    }    
    

When you're ready, move on to Functions Exercises

Continue

Creative Commons License