Question presented to candidate: "What's the actual difference between var, let, and const — not just 'let is block-scoped' — and can you show me a real bug that happens if you use var inside a loop with a callback?"
What a strong answer should cover:
- 📌 Interview term: function scope (
var) — avardeclared anywhere inside a function is accessible throughout the entire function, ignoring block boundaries likeiforfor. Verified directly: avardeclared inside anifblock is still readable after the block ends. - 📌 Interview term: block scope (
let/const) — alet/constis only accessible within the nearest enclosing block ({}). Verified directly: accessing it outside the block throws a realReferenceError. - 📌 Interview term: the Temporal Dead Zone (TDZ) —
let/constare hoisted to the top of their scope likevar, but remain uninitialized until their declaration line executes; accessing them before that point throws a realReferenceError("Cannot access before initialization"), distinct fromvar, which is hoisted AND initialized toundefined— verified directly, a real, observable difference. - 📌 Interview term: the classic
var-in-loop-closure bug — verified directly with realsetTimeoutcallbacks: afor (var i ...)loop's callbacks all see the SAME final value ofi(becausevarhas one shared binding for the whole loop), whilefor (let i ...)gives each callback its OWN per-iteration binding, seeing the value at the time of its own iteration. - A precise answer names that
constprevents reassignment of the binding, not mutation of the value — verified directly: reassigning aconstthrows, but mutating aconst-bound object's properties succeeds fine.
Clarifying questions expected:
- None — this is a definitional/comparison question; producing the real closure bug live is the strongest possible signal.
Code / implementation expected: Yes — reproducing the var-vs-let loop-closure bug with real setTimeout callbacks is the single most convincing demonstration of genuine understanding.