Output Questions: Hoisting & Scope

Predict the output โ€” var/let/const hoisting, TDZ, function declaration vs expression, and block scope gotchas.

must medium โฑ 25 min hoistingscopevarletconsttdzfunction-declaration
Mastery:
Why interviewers ask this
Hoisting output questions appear in virtually every JS screen. They test whether you truly understand how the engine processes declarations before running code.

For each block below, predict the output before running it. Each question is a real interview pattern.


Q1 โ€” var hoisting: declaration but not initialization

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
undefined
5

var declarations are hoisted to the top of their function/global scope but their value is not. The engine treats it as:

var a;          // hoisted โ€” exists but is undefined
console.log(a); // undefined
a = 5;
console.log(a); // 5

Q2 โ€” let/const: Temporal Dead Zone

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
ReferenceError: Cannot access 'x' before initialization

let and const are hoisted but placed in the Temporal Dead Zone โ€” they exist but are inaccessible until the declaration line is evaluated. Accessing them before that line throws a ReferenceError.


Q3 โ€” function declaration vs expression

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
"foo"
undefined
  • Function declarations are fully hoisted โ€” both the name and the body. You can call foo() before its definition.
  • Function expressions (var bar = functionโ€ฆ) hoist only the var bar โ€” the variable is undefined until that line runs. Calling bar() before line 5 throws TypeError: bar is not a function.

Q4 โ€” var in a block scope

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
1
"undefined"

var ignores block scope โ€” x leaks out of the block into the surrounding function/global scope. let is block-scoped, so y doesnโ€™t exist outside the {}. typeof on an undeclared variable returns "undefined" (not a ReferenceError โ€” a special case).


Q5 โ€” hoisting order: function vs var with same name

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
"function"
"string"

When a variable and a function declaration share the same name, the function declaration wins during hoisting โ€” it takes precedence over var. So before any code runs, foo is the function. After foo = 'variable' runs on line 2, foo becomes the string.


Q6 โ€” function declaration inside a block (non-strict mode)

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
"undefined"   (outer, before block)
"function"    (inside block, after hoisting within block)
"function"    (inside block, after declaration)
"function"    (outer, after block โ€” hoisted out in non-strict mode)

In non-strict mode, function declarations inside blocks have complex legacy behavior: the declaration is hoisted to the top of the block, but the name is also created in the outer scope (as undefined initially), and when execution passes the declaration line, the outer binding is updated. This is an engine-specific quirk โ€” in strict mode, block-scoped functions stay in the block. Avoid block-level function declarations.


Q7 โ€” var in a for loop

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
3
3
3

var is function-scoped โ€” there is one shared i across all iterations. By the time the setTimeout callbacks fire (after the loop completes), i is 3. All three closures close over the same binding.


Q8 โ€” let fixes the loop bug

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
0
1
2

let is block-scoped and creates a fresh binding per iteration. Each callback closes over its own independent j.


Q9 โ€” hoisting with re-declaration

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
2
2

Both var a declarations are in the same function/global scope โ€” var allows re-declaration and they both refer to the same variable. The inner block updates the shared a to 2, so both logs print 2.


Q10 โ€” const in a block

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
2
1

const is block-scoped โ€” the inner const x = 2 is a completely separate binding from the outer const x = 1. No error; each block has its own x.


Q11 โ€” function parameter shadowing

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
"local"
"global"

The parameter x shadows the outer x within the function body. The global x is untouched.


Q12 โ€” class declaration hoisting

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
ReferenceError: Cannot access 'MyClass' before initialization

class declarations are hoisted but placed in the TDZ, just like let/const. You cannot use a class before its declaration. (The second console.log never runs.)

The pattern to remember
Hoisting order of precedence: function declarations beat var. Both are hoisted, but function declarations bring their body; var brings undefined. let, const, and class are in the TDZ. var escapes blocks; everything else doesnโ€™t.

Likely follow-up questions
  • Why does accessing a let variable before its line throw but var doesn't?
  • What is the Temporal Dead Zone?
  • What's the difference between a function declaration and a function expression for hoisting?
  • Why can you call a function before it's defined when using function declarations?

References