While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Inevitably, this means that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.
Of note however, is the fact that the hoisting mechanism only moves the declaration. The assignments are left in place.
If you’ve ever wondered why you were able to call functions before you wrote them in your code, then read on!
undefined vs ReferenceError
Before we begin in earnest, let’s deliberate on a few things.
This brings us to our first point of note:
Our second point is:
As we mentioned before, all variable and function declarations are hoisted to the top of their scope. I should also add that variable declarations are processed before any code is executed.
However, in contrast, undeclared variables do not exist until code assigning them is executed. Therefore, assigning a value to an undeclared variable implicitly creates it as a global variable when the assignment is executed. This means that, all undeclared variables are global variables.
To demonstrate this behaviour, have a look at the following:
The scope of a variable declared with the keyword var is its current execution context. This is either the enclosing function or for variables declared outside any function, global. Let’s look at a few examples to identify what this means:
We expected the result of the log to be: ReferenceError: hoist is not defined , but instead, its output is undefined .
Why has this happened?
This discovery brings us closer to wrangling our prey.
Because of this, we can use variables before we declare them. However, we have to be careful because the hoisted variable is initialised with a value of undefined. The best option would be to declare and initialise our variable before use.
Function scoped variables
As we’ve seen above, variables within a global scope are hoisted to the top of the scope. Next, let’s look at how function scoped variables are hoisted.
Take an educated guess as to what our output might be.
If you guessed, undefined you’re right. If you didn’t, worry not, we’ll soon get to the bottom of this.
This is how the interpreter views the above code:
The variable declaration, var message whose scope is the function hoist() , is hoisted to the top of the function.
To avoid this pitfall, we would make sure to declare and initialise the variable before we use it:
Running our code in strict mode:
We enable strict mode by prefacing our file or function with
Let’s test it out.
We can see that instead of assuming that we missed out on declaring our variable, use strict has stopped us in our tracks by explicitly throwing a Reference error . Try it out without use strict and see what happens.
Strict mode behaves differently in different browsers however, so it’s advisable to perform feature testing thoroughly before relying on it in production.
ECMAScript 6, ECMAScript 2015 also known as ES6 is the latest version of the ECMAScript standard, as the writing of this article, Jan 2017 and introduces a few changes to es5.
Before we start, to be noted is the fact that variables declared with the keyword let are block scoped and not function scoped. That’s significant, but it shouldn’t trouble us here. Briefly, however, it just means that the variable’s scope is bound to the block in which it is declared and not the function in which it is declared.
Let’s start by looking at the let keyword’s behaviour.
Like before, for the var keyword, we expect the output of the log to be undefined . However, since the es6 let doesn’t take kindly on us using undeclared variables, the interpreter explicitly spits out a Reference error.
This ensures that we always declare our variables first.
However, we still have to be careful here. An implementation like the following will result in an ouput of undefined instead of a Reference error .
Hence, to err on the side of caution, we should declare then assign our variables to a value before using them.
The const keyword was introduced in es6 to allow immutable variables. That is, variables whose value cannot be modified once assigned.
With const , just as with let , the variable is hoisted to the top of the block.
Let’s see what happens if we try to reassign the value attached to a const variable.
How does const alter variable declaration? Let’s take a look.
Much like the let keyword, instead of silently exiting with an undefined , the interpreter saves us by explicitly throwing a Reference error .
The same occurs when using const within functions.
With const , es6 goes further. The interpreter throws an error if we use a constant before declaring and initialising it.
Our linter is also quick to inform us of this felony:
Therefore, a constant variable must be both declared and initialised before use.
- Function declarations
- Function expressions
We’ll investigate how hoisting is affected by both function types.
Function expressions, however are not hoisted.
Let’s try the combination of a function declaration and expression.
As we can see above, the variable declaration var expression is hoisted but it’s assignment to a function is not. Therefore, the intepreter throws a TypeError since it sees expression as a variable and not a function.
Order of precedence
- Variable assignment takes precedence over function declaration
- Function declarations take precedence over variable declarations
Function declarations are hoisted over variable declarations but not over variable assignments.
Let’s take a look at what implications this behaviour has.
Variable assignment over function declaration
Function declarations over variable declarations
- Class declarations
- Class expressions
I’m sure you’ve noticed that instead of getting an undefined we get a Reference error . That evidence lends claim to our position that class declarations are hoisted.
If you’re paying attention to your linter, it supplies us with a handy tip.
So, as far as class declarations go, to access the class declaration, you have to declare first.
Much like their function counterparts, class expressions are not hoisted.
Here’s an example with the un-named or anonymous variant of the class expression.
Here’s an example with a named class expression.
The correct way to do it is like this:
Let’s summarise what we’ve learned so far:
- While using es5 var, trying to use undeclared variables will lead to the variable being assigned a value of undefined upon hoisting.
- While using es6 let and const, using undeclared variables will lead to a Reference Error because the variable remains uninitialised at execution.
Want to learn more? Join the DigitalOcean Community!
Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.