Interview answer drill

Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Is JavaScript block-scoped or function-scoped?Frontend interview answer

HighIntermediateJavascript
Interview focus

This JavaScript interview question tests whether you can explain JavaScript scope: var vs let vs const, TDZ, and loop bug pitfalls, connect it to production trade-offs, and handle common follow-up questions.

  • JavaScript scope: var vs let vs const, TDZ, and loop bug pitfalls explanation without falling back to memorized docs wording
  • Scope and Block Scope reasoning, edge cases, and production failure modes
  • How you would answer the most likely JavaScript interview follow-up
Practice more JavaScript interview questions
Interview quick answer

JavaScript is both block-scoped and function-scoped depending on the declaration keyword. Strong answers compare var, let, and const side by side, show loop-capture and switch-case pitfalls, and explain how TDZ fits into block scope.

Full interview answer

Short answer first

JavaScript is both, depending on what you declare with. That is the short answer. The real interview follow-up is which keyword created the binding, when var leaks across blocks, and why let/const can still throw before the declaration line because of the TDZ.

So the right interview answer is not one word. It is: 'Both, depending on declaration keyword.'

Keyword

Scope type

Redeclare in same scope?

Access before declaration

Typical bug

var

Function scope

Yes

Allowed (value is undefined due to hoisting)

Leaking outside blocks and loop-closure issues

let

Block scope

No

ReferenceError (TDZ)

Expecting it to behave like var

const

Block scope

No

ReferenceError (TDZ)

Assuming object values become immutable

Scope behavior is primarily keyword-driven in modern JavaScript.
JAVASCRIPT
if (true) {
  var a = 1;
  let b = 2;
  const c = 3;
}

console.log(a); // 1  (var escapes block)
// console.log(b); // ReferenceError
// console.log(c); // ReferenceError
                  

Follow-up one-liner

Say it this way: JavaScript is function-scoped for var and block-scoped for let/const. TDZ is part of that block scope: the binding exists for the whole block, but you cannot use it until initialization runs.

Loop behavior: the classic interview trap

var in loops creates one shared binding for all iterations. let creates a fresh binding per iteration. This is why asynchronous callbacks behave differently.

JAVASCRIPT
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log('var', i), 0);
}
// var 3
// var 3
// var 3

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log('let', j), 0);
}
// let 0
// let 1
// let 2
                  

Function scope vs block scope in real code

A function body is also a block, but var ignores inner blocks inside that function and stays visible across the entire function body. let/const stay where they are declared.

JAVASCRIPT
function demo(flag) {
  if (flag) {
    var status = 'ok';
    let temp = 42;
  }

  console.log(status); // 'ok' when flag=true
  // console.log(temp); // ReferenceError
}

demo(true);
                  

Switch/case pitfall

All case clauses share one switch block unless you add braces. In production this shows up when reducers, parsers, or command handlers reuse the same variable name in multiple cases. With let/const, duplicate names across cases can throw errors unless each case is wrapped in its own block.

JAVASCRIPT
const kind = 'a';

switch (kind) {
  case 'a': {
    const msg = 'A';
    console.log(msg);
    break;
  }
  case 'b': {
    const msg = 'B';
    console.log(msg);
    break;
  }
}
                  

Global nuance: Script vs Module

In classic scripts, top-level var can attach to window. In ES modules, top-level bindings are module-scoped and do not become window properties. This matters when old script assumptions meet bundlers, test runners, or interview follow-ups.

JAVASCRIPT
// Classic script (non-module)
var legacy = 1;
let modern = 2;

console.log(window.legacy); // 1
console.log(window.modern); // undefined
                  

Practical rules

  1. Default to const; use let only when reassignment is required.
  1. Avoid var in modern codebases unless maintaining legacy code.
  1. For loops with async callbacks, prefer let.
  1. In interviews, say 'JavaScript supports both block and function scope' and then explain keyword behavior.

Practical scenario
A dashboard uses var inside loops that register click handlers, causing every handler to point at the last row index in production.

Common pitfalls

  • Mixing var and let in the same function.
  • Ignoring TDZ behavior during refactors.
  • Assuming switch cases create separate scope automatically.
Trade-off or test tip
Add tests for loop callbacks and branch-local variables, especially when migrating old code from var to let/const.

Still so complicated?

Think of var as office-wide access in a function floor, while let/const are room-specific badges. Same building, different permission boundaries.

Summary

JavaScript is not purely block-scoped or purely function-scoped. It is keyword-dependent: var is function-scoped; let/const are block-scoped. Most production bugs come from legacy var assumptions in loops, conditionals, and mixed old/new code.

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the JavaScript interview questions cluster or a guided prep path.