Understanding JavaScript hoisting

John John (304)
0

In short, hoisting is when JavaScript moves variable and function declarations to the top of their scope before any code is executed. Anyone who has some experience with JavaScript has surely seen the effects of hoisting even if by accident. In my case, I've learned to expect the outcome of hoisting without truly understanding it. In this guide, I will aim to cover JavaScript hoisting with examples and explanations.

Posted in these interests:
h/code69 guides
h/javascript27 guides

At some point, you may have been wondered about the difference between this:

var hello = function() {
    alert('hello');
};
and
function hello() {
    alert('hello');
}
In order to understand JavaScript hoisting you must understand the difference between the first, a function expression, and the second, a function declaration. The most important difference is that function declarations (and declarations in general) are processed before any code is executed. That means that declaring a function:
function hello() {
    alert('hello');
}
or a variable:
var x = 1;
make these names 'hello' and 'x' available in their scope no matter where they are declared. Because these declarations are processed before any code is executed, declaring them at the bottom of a scope is the same is declaring it at the top. In fact, hoisting literally moves these declarations to the top of the scope, and we'll cover that in more detail later. Inversely, expressions are not hoisted and they are processed when the code is executed, not before. So you can see the following will throw an error:
x();
var x = function() {
    alert('I am x');
}
whereas this will work as expected:
x();
function x() {
    alert('I am x');
}
Before moving on, you should be able to identify a function declaration and a variable declaration, and you should understand that declarations are processed before any code is executed.

Now you must understand the difference between a declaration and an initialization. JavaScript hoisting applies to declarations NOT initializations. So the following:

var x = 7;
is both a declaration and an initialization. The variable 'x' is declared like this:
var x
and initialized like this:
x = 7
Understand that declarations alone are hoisted, not initializations. So according to our definition, the declaration var x is processed before any code is executed, while the initialization x = 7 is executed where it is initialized. However, with a function declaration the entire function is processed in advance of any code execution. You will see more examples of these later.

When we say that a declaration is processed before any code is executed, that means they are essentially hoisted to the top of the scope. So in the following:

hello();
function hello() {
   alert('hello');
}
the hello function is hoisted to the top of the scope and processed like this:
function hello() {
    alert('hello');
}
hello();
And in this:
if (x) {
    alert(x);
} else {
    alert('not initialized yet');
}
var x = 7;
the variable declaration x is hoisted to the top, the initialization remains in place, and it is processed like this:
var x;
if (x) {
    alert(x);
} else {
    alert('not initialized yet');
}
x = 7;
It won't throw an error because the declaration of x has been hoisted to the top, but it will alert "not initialized yet" because x is initialized after.

If you understand hoisting then you should be able to predict the following outcomes:

alert(foo());
function foo() {
    function bar() {
        return 10;
    }
    return bar();
    function bar() {
        return 20;
    }
}
if you said 20 you are correct. The previous code would be processed like this:
function foo() {
    function bar() {
        return 10;
    }
    function bar() {
        return 20;
    }
    return bar();
}
alert(bar());
After hoisting has taken place, you can more easily see that the function foo would return 20. How about this one:
var x = 7;
var add = function() {
    return x + y;
}
alert(add());
var y = 8;
If you answered "NaN" (not a number) then you are correct. This is because the function add is trying to add the variable x with the variable y which hasn't yet been initialized. Even though it's been declared, it hasn't yet been initialized. The previous code would be processed like this:
var x = 7;
var add;
var y;
add = function() {
    return x + y;
}
alert(add());
y = 8;
You can see that by the time add is actually called, y has no value so it cannot be added to x which was initialized with a value of 7. Here's one more:
alert(words());
function words() {
    function getWords() {
        return "i am a function declaration";
    }
    var getWords = function() {
        return "i am a function expression";
    }
    return getWords();
}
By now you can probably predict that this program will alert "i am a function expression". After hoisting this will be processed like so:
function words() {
    function getWords() {
        return "i am a function declaration";
    }
    var getWords;
    getWords = function() {
        return "i am a function expression";
    }
    return getWords();
}
alert(words());
Not much changes in the order, but you can see that the getWords function variable name overwrites the getWords function declaration when the code is actually executed.

There are a few things you can take away from this. If you understand hoisting, you should able to prevent hoisting related confusion for yourself or anyone else reading your code in the future. One way to do this is to write your code with hoisting in mind. Make sure to declare your variables and functions at the top of the scope. Since hoisting will essentially do this for you at run-time, you will avoid confusion if you write your code in the order that it will be processed. With this you will be able to plainly see if a variable used in a function hasn't yet been initialized or if the value changes before the function gets called. Next, know when to use function declarations as opposed to function expressions. In some cases, it doesn't really matter, but you should use them intentionally. Understand that function definitions are created before the code is ever executed and a function expression is created in place when the code is executed. It is generally recommended to use function declarations by default and use function expressions when there is a need. One case where you would want to use a function expression is when you are creating a function conditionally. Take a look at the following example:

if (true) {
    function foo() {
        return 'true';
    }
} else {
    function foo() {
        return 'false';
    }
}
alert(foo());
Different browsers handle this case differently, but chances are your browser will alert 'false'! After learning about JavaScript hoisting, this probably makes sense. In this case, you should use function expressions:
var foo;
if (true) {
    foo = function() {
        return 'true';
    };
} else {
    foo = function() {
        return 'false';
    };
}
alert(foo());
Declaring foo at the top of the scope will ensure that this variable remains local. Using function expressions will ensure that the variable foo is initialized when the code is executed rather than being hoisted before. If you've made it this far in the guide, I'm interested to know if you have any other concerns related to hoisting and if you have any other guidelines for when to use function declarations vs. expressions. If so, leave a comment below!

John John (304)
0

A scope is simply the set of variables that you have access to. Scoping in JavaScript can be confusing. Even some experienced JavaScript developers still struggle with it.