What exactly gets hoisted in JavaScript?
The earlier questions in this topic covered the basic types and the way JavaScript converts values. This answer looks at hoisting, which explains why you can use a name before the line where you declared it, and it is one of the questions that comes up in almost every interview.
Hoisting is not a separate step that you can watch happen. It is the natural result of how the engine prepares a scope before your code runs. When a scope is entered, the engine creates a binding for every declaration that it can find in that scope, and it does this before a single statement runs. What differs between the kinds of declaration is what happens to that binding at the moment it is created.
console.log(typeof hoistedVar); // 'undefined' - the binding exists but has no value yet
console.log(hoistedFn()); // works - the whole declaration was lifted
var hoistedVar = 'assigned later';
function hoistedFn() {
return 'I am ready';
}
For a var declaration, the binding is created and it is immediately given the empty value, so reading it before the assignment line gives you that empty value instead of an error. For a function declaration, the whole function, body and all, is created up front, so you can call it from a line that is above its definition. For let and const, the binding is created but it is deliberately left empty, and touching it before the declaration runs throws a ReferenceError. That window is called the temporal dead zone.
Class declarations are hoisted in name only. The binding exists, so the engine knows the name, but the class itself is not created until the declaration is reached, and reading the name earlier throws the same ReferenceError that a let throws in its dead zone.
One point that people often get wrong is that hoisting belongs to a function or a module scope, and not to the whole file. A declaration inside a block is lifted to the top of that block, not to the top of the function, and the engine only knows about it by scanning the block before it runs.