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.