Hoisting is a key concept in JavaScript that can cause unexpected behavior in your code if you're not aware of how it works.
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 or const)The
console.log
will print undefined as the assignment of the value 1 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 return undefined . This behaviour is called Type 2 hoisting behaviour by MDN.
- ES6: For let and const declarations are also hoisted to the top of their scope, but they are not initialized with a value of undefined.
Practically, this means you cannot use a let or const variable before it's declared, or you will get a ReferenceError. 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 or let 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:
@aravind030792 var hoisting was thus unintended consequence of function hoisting, no block scope, JS as a 1995 rush job. ES6 'let' may help.
— BrendanEich (@BrendanEich) October 15, 2014
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:
@aravind030792 var hoisting was thus unintended consequence of function hoisting, no block scope, JS as a 1995 rush job. ES6 'let' may help.
— BrendanEich (@BrendanEich) October 15, 2014
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.
Some great resources on Hoisting/reading material related to the topic:
Top comments (0)