Predict JavaScript let and var Behavior

Read the full interview experience this question came from →

Quick Overview

Predict exact JavaScript behavior for let and var by reasoning about block versus function scope, hoisting, temporal dead zones, redeclaration, and per-iteration loop closures.

Predict JavaScript let and var Behavior

Company: Amazon

Role: Frontend Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

Explain how `let` and `var` differ in scope, hoisting, redeclaration, and loop closures. Then predict the exact output or error for each practice snippet below and justify every line. ```javascript function scopeDemo() { console.log(x); var x = 1; if (true) { let y = 2; var z = 3; } console.log(z); console.log(y); } scopeDemo(); ``` ```javascript const a = []; for (var i = 0; i < 3; i++) a.push(() => i); const b = []; for (let j = 0; j < 3; j++) b.push(() => j); console.log(a.map(f => f()).join(",")); console.log(b.map(f => f()).join(",")); ``` ### Constraints & Assumptions - Assume a modern JavaScript runtime and that each snippet runs independently. - The first snippet runs inside a normal function; the second may run as a script or module because its relevant bindings are local to the loops. - Distinguish declaration hoisting from initialization and distinguish a thrown error from printed output. ### Clarifying Questions to Ask - Does execution stop after the first uncaught runtime error? - Are the callbacks invoked during each iteration or after both loops have completed? - Would a top-level binding distinction matter to either result? ### What a Strong Answer Covers - Function scope for `var` and block scope for `let`. - `var` initialization to `undefined` versus the temporal dead zone for `let`. - The first snippet's printed values and the exact point at which its `ReferenceError` stops execution. - One shared `var` loop binding versus a fresh per-iteration `let` binding in the second snippet. - Redeclaration and global-object differences as relevant context, without substituting them for the line-by-line predictions. ### Follow-up Questions - How does `const` compare with `let` for binding and object mutation? - Why does an immediately invoked function fix the classic `var` loop closure bug? - How do top-level bindings differ in an ES module?

Overview: Predict exact JavaScript behavior for let and var by reasoning about block versus function scope, hoisting, temporal dead zones, redeclaration, and per-iteration loop closures.

Read the full Amazon Frontend Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Amazon
Amazon logo
Amazon
Aug 22, 2026
mediumFrontend EngineerOnsiteSoftware Engineering Fundamentals
1
0

Explain how let and var differ in scope, hoisting, redeclaration, and loop closures. Then predict the exact output or error for each practice snippet below and justify every line.

function scopeDemo() {
  console.log(x);
  var x = 1;
  if (true) {
    let y = 2;
    var z = 3;
  }
  console.log(z);
  console.log(y);
}
scopeDemo();
const a = [];
for (var i = 0; i < 3; i++) a.push(() => i);
const b = [];
for (let j = 0; j < 3; j++) b.push(() => j);
console.log(a.map(f => f()).join(","));
console.log(b.map(f => f()).join(","));

Constraints & Assumptions

  • Assume a modern JavaScript runtime and that each snippet runs independently.
  • The first snippet runs inside a normal function; the second may run as a script or module because its relevant bindings are local to the loops.
  • Distinguish declaration hoisting from initialization and distinguish a thrown error from printed output.

Clarifying Questions to Ask Guidance

  • Does execution stop after the first uncaught runtime error?
  • Are the callbacks invoked during each iteration or after both loops have completed?
  • Would a top-level binding distinction matter to either result?

What a Strong Answer Covers Guidance

  • Function scope for var and block scope for let .
  • var initialization to undefined versus the temporal dead zone for let .
  • The first snippet's printed values and the exact point at which its ReferenceError stops execution.
  • One shared var loop binding versus a fresh per-iteration let binding in the second snippet.
  • Redeclaration and global-object differences as relevant context, without substituting them for the line-by-line predictions.

Follow-up Questions Guidance

  • How does const compare with let for binding and object mutation?
  • Why does an immediately invoked function fix the classic var loop closure bug?
  • How do top-level bindings differ in an ES module?
Loading comments...