Is JavaScript block-scoped or function-scoped?

HighIntermediateJavascript
Quick Answer

JavaScript uses both block scope and function scope depending on declaration type. var is function-scoped, while let and const are block-scoped. Strong answers explain loop behavior, closure bugs, switch-case pitfalls, and module-vs-script global scope differences.

Answer

Short answer first

JavaScript is both, depending on what you declare with:

  • var is function-scoped (or global if declared outside a function).
  • let and const are block-scoped (inside { ... }).

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
                  

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. 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 in bundlers and 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

    • Default to const; use let only when reassignment is required.
    • Avoid var in modern codebases unless maintaining legacy code.
    • For loops with async callbacks, prefer let.
    • 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 the relevant interview-question hub first, then move into a concrete study plan before targeted company sets.