TL;DR
All declarations (
var,let,const,function,function*,class) are hoisted in JavaScript. The difference betweenvar/function/function*declarations andlet/const/classdeclarations is the initialisation.
First, let’s understand the definition of hoisting in JavaScript:
Let’s call the var/function/function* group as group 1, and the remaining group as group 2. When the JS engine reads through the JS code, it will find all bindings in the code and declare those bindings. However, for group 1, they are initialised immediately when the binding is created with the value undefined, while group 2 remains in an uninitialised state. After the JS engine executes the declaration line of that binding (where the let/const/class statement is written), then that binding is initialised - meaning if you try to access that binding before it is initialised, a ReferenceError will occur.
var x = "global",
y = "global";
(function() {
x; // undefined - this is normal hoisting in JS
y; // Reference error: y is not defined - y is still hoisted to the top of the function scope
// But instead of receiving the value undefined, the JS engine will throw an error if we try
// to access variable y before the JS engine executes the let y = ... statement on line 12
// some code here...
var x = "local";
let y = "local";
}());