
JavaScript Hoisting
Planted:
Status: decay
If you are unfamiliar with the terminology below, check out:
An identifier is a sequence of characters in the code that identifies a variable
, function
, or property
.
For example: the myVar
string in var myVar = 1
.
Every time the compiler enters a scope, it starts registering declarations.
Each identifier will be moved to the top of a scope.
This is known as hoisting.
What scope the identifier moves to the top of depends on the type of declaration:
- ▪
function
, - ▪
var
or - ▪
let
,const
andclass
.
function
A function
declared identifier will move to the top of the nearest block scope.
If none available, the nearest parent scope.
It will also be initialized to its associated function reference and can be used.
Below is an example of calling a function before it is declared.
It works because it is hoisted to the top of the global scope by the compiler before line: 1
is executed.
Console
func() function func() { console.log('called') }
This only applies to formal functions (functions declared with the function
keyword), not function expressions:
- ▪
const func = () => { .. }
or - ▪
const func = function() { .. }
.
var
A var
declared identifier will move the top of the nearest function scope.
If none available, the global or module scope.
It will also be initialized to its default value of undefined
and can be used.
Even if you declare and initialize the variable on the same line, var myVar = 1
, only the identifier is hoisted.
The variable will be initialized to its correct value when the line of code is executed.
Console
console.log(myVar) var myVar = 1; console.log(myVar)
Below is an example of a variable declared and initialized in a block scope.
Because it's declared as a var
, it is hoisted to the function scope.
This makes it accessible to the console.log
.
Console
function myFunc(x) { if (x === 1) { var y = 1 } console.log(y) } myFunc(1)

const, let and class
A const
, let
or class
declared identifier will move the top of the nearest block scope.
If none available, the nearest parent scope.
It will also be initialized to its default value of uninitialized
.
It will remain uninitialized until the compiler executes the line where is it declared.
Attempting to use it before then results in an error.
Console
console.log(myVar) let myVar = 1;
TDZ
The above is an example of The Temporal Dead Zone.
A window of time where a variable is declared but uninitialized.
When a var
is hoisted, it is initialized with a default value of undefined
, so it has a TDZ of 0 in length and unobservable.
let
, const
and class
have an observable TDZ.
Console
console.log(myVar) let myVar = 1
TDZ errors can be avoided by always putting let
, const
and class
declarations at the top of their scope.
This shrinks the TDZ to 0 length.
If the declaration isn't needed from the beginning, create an inner block scope:
Instead of this:
/index.js
function add1IfEven(x) { if (!x) { throw new Error("Input required") } const isEven = x%2 === 0 return isEven ? x + 1 : x }
Do this:
/index.js
function add1IfEven(x) { if (!x) { throw new Error("Input required") } { const isEven = x%2 === 0 return isEven ? x + 1 : x } }

Rules of Scope
Rules of scope, such as hoisting, are applied per scope instance.
During execution, each time a scope is entered, everything resets.
This is why using a const
within a loop doesn't throw aa already declared error after the 1st iteration.
Console
for (let i = 0; i < 2; i++) { const myVar = i console.log(myVar) }