Lesson 10 of 80
JavaScript scans a scope for declarations before running a single line of it — which is why some code works before the line that 'creates' it, and other code throws instead.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
What happens when you run `console.log(x); let x = 5;`?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Before running any code in a scope, the JS engine does a quick pass over it and registers every declaration it finds — var, function, let, const, class. That registration step is hoisting. It's not that your code physically moves to the top; the name is known early, but what happens if you use it early depends entirely on which keyword declared it.
console.log(a); // undefined — declared AND initialized to undefined
var a = 1;
console.log(b); // ReferenceError — "Cannot access 'b' before initialization"
let b = 2;
console.log(sayHi()); // works — function declarations hoist fully, body included
function sayHi() {
return "hi";
}
var is hoisted and pre-initialized to undefined. let/const are hoisted too — the name is reserved — but they sit in the temporal dead zone (TDZ) until their declaration line actually runs, so touching them early throws instead of silently returning undefined. Function declarations hoist their entire body, which is why you can call one before its line in the file.
Before running a single line, the engine scans the scope and registers every declaration it finds.
let/const are hoisted too. The common shorthand "let and const aren't hoisted" is wrong — they're hoisted but land in the TDZ, which is a different failure mode (throw) than var's (undefined), not an absence of hoisting.const sayHi = function () {} hoists the const binding (into the TDZ), not the function body — calling it early throws, unlike a plain function sayHi() {}.let/const — using a class before its line throws, it doesn't silently work."What does this log?" snippets mixing var/let/function calls above their declaration are a standard warm-up question — they're really testing whether you know hoisting has three distinct behaviours, not one.