Hoisting is a key concept in JavaScript that can cause unexpected behavior in your code if you’re not aware of how it works.
At a high level, hoisting refers to the process of moving variable and function declarations to the top of their respective scopes before your code is executed.
JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, prior to execution of the code. — MDN
Hoisting Variables
- If you declare a variable after its first usage, this variable declaration is “hoisted” to the top of its scope.
- If you assign a value to the variable, then the order it is assigned is unchanged and is unaffected by the hoisting.
See the following simple example.
function myFunction() {
console.log(myHoistedVariable); // undefined for ES5 and below
var myHoistedVariable= 1;
console.log(myHoistedVariable); // 1
}
- Since the declaration of
myHoistedVariable
is hoisted, the firstconsole.log
will attempt to print out the value (Note. the actual result will differ depending on if you usevar
,let
orconst
) - The
console.log
will printundefined
as the assignment of the value1
isn’t moved along with the hoisting (Note. this is for ES5 only usingvar
).
The above example, basically is the same as the following code:
function myFunction() {
var myHoistedVariable
console.log(myHoistedVariable); // undefined for ES5 and below or ReferenceError for ES6 `let` or `const`
myHoistedVariable = 1;
console.log(myHoistedVariable); // 1
}
Note. ES5 v ES6
While hoisting technically still occurs with ES6, in practical effects you will be prevented from using it.
- ES5: If you are using
var
a hoisted value will returnundefined
. This behaviour is called Type 2 hoisting behaviour by MDN. - ES6: For
let
andconst
declarations are also hoisted to the top of their scope, but they are not initialized with a value ofundefined
. Practically, this means you cannot use alet
orconst
variable before it's declared, or you will get aReferenceError
. This is called Type 3 hoisting behaviour by MDN.
If you declare a JavaScript file with 'use strict';
this will mean that hoisting using var
will throw a Type 3 ReferenceError
. (See this article from Digital Ocean for more info)
Hoisting Functions
Hoisting for functions get a bit more interesting:
- Function declarations are hoisted to the top of their scope. This means that you can call functions before they are declared.
myHoistedFunction();
function myHoistedFunction() {
console.log("Hi I'm hoisted!");
}
- Function expressions are not hoisted. This means that if you declare a function expression using
const
orlet
you cannot call the function before this declaration. This is also the case for arrow functions as they are also function expressions.
Why hoist at all??? 🤔
If this all seems pretty random and unnecessary, it did to me too — especially hoisting variables.
Why wouldn’t you just enforce everyone to declare variables, functions and classes at the top of their scope? Even though this is largely corrected in ES6 and the move towards function expressions as the conventional norm (another hot topic for another day).
Historical reasons
Digging into this a bit further I found this tweet from the archives:
This article on Quora explains this further. TL;DR Brendan Eich when implementing JavaScript (AKA LiveScript) in 10 days wanted to avoid the painful top to bottom enforcement of function declarations in ML languages like Lisp.
Brendan also followed up with a following tweet clarifying that var
hoisting was unintended:
From this context — the feature does make sense.
Interpreter performance reasons
When the JavaScript engine compiles code, it first goes through a process called parsing, where it breaks down code into smaller, more manageable pieces.
By moving function declarations to the top of their scope before execution, the interpreter can avoid the need to search for the declaration of the function each time it is called. Instead, it can simply execute the function directly from memory, which can be faster and more efficient.
To summarise, it appears there are two key benefits of Hoisting:
- Allow you to use function declarations before they are defined. This allows you to organise where you put your function code regardless of where they are called as long as they are within the same scope.
- Improve the performance of the interpreter. Because variable declarations are moved to the top of their scope during compilation this may reduce the time the JavaScript engine needs to parse the code.