DEV Community

ValPetal Tech Labs
ValPetal Tech Labs

Posted on

Question of the Day #17 [Talk::Overflow]

This post explains a quiz originally shared as a LinkedIn poll.


🔹 The Question

let count = 0;

function increment() {
  count++;
  var count;
  return count;
}

console.log(increment());
console.log(count);
Enter fullscreen mode Exit fullscreen mode

Hint: Think about where var declarations end up before any code runs — and what happens when you increment something that doesn't have a value yet.

Follow me for JavaScript puzzles and weekly curations of developer talks & insights at Talk::Overflow: https://talkoverflow.substack.com/


🔹 Solution

Correct answer: B) NaN, 0

The first console.log prints NaN. The second prints 0.

🧠 How this works

This quiz targets a subtle but dangerous interaction between var hoisting and variable shadowing.

When JavaScript parses the increment function, the var count; declaration is hoisted to the top of the function scope — even though it appears after count++ in the source code. This creates a local count variable inside increment, completely separate from the outer let count = 0.

At runtime, the function effectively becomes:

function increment() {
  var count;       // hoisted — initialized to undefined
  count++;         // undefined + 1 → NaN
  return count;    // NaN
}
Enter fullscreen mode Exit fullscreen mode

The outer let count = 0 is never touched because the local var count shadows it entirely within the function.

This is a real production bug that typically appears when developers:

  • Accidentally add a var declaration for a variable they intended to reference from an outer scope
  • Refactor code by moving variable declarations around without realizing var hoists to the function boundary
  • Copy-paste code fragments that introduce an unintended local declaration

🔍 Line-by-line explanation

  1. let count = 0; — Declares count in the outer (module/script) scope with value 0.

  2. function increment() { — JavaScript parses the entire function body before executing. It finds var count; and hoists it to the top of the function, initializing it to undefined. This local count shadows the outer count.

  3. count++; — Operates on the local count, which is undefined. The ++ postfix operator converts undefined to NaN via Number(undefined), then attempts NaN + 1, which is NaN. The local count is now NaN.

  4. var count; — This is a no-op at runtime. The declaration was already hoisted; no new assignment happens here.

  5. return count; — Returns the local count, which is NaN.

  6. console.log(increment()); — Prints NaN.

  7. console.log(count); — Prints 0. The outer count was never modified because the function only ever operated on its own local count.


🔹 Key Takeaways

  1. var declarations are hoisted to the top of their enclosing function — not to the line where they appear. This means a var anywhere in a function creates a local variable that exists from the very first line of that function.

  2. Hoisted var variables shadow outer variables silently. Unlike let/const which would cause a clear TDZ error if accessed before declaration, var quietly creates an undefined binding that masks the outer scope.

  3. undefined++ evaluates to NaN, not an error. JavaScript's type coercion converts undefined to NaN before the arithmetic operation, producing a silent wrong result rather than a crash.

  4. Always prefer let/const over var. With let or const, attempting to use the variable before its declaration throws a ReferenceError (Temporal Dead Zone), making this class of bugs immediately visible instead of silently corrupting data.

  5. Linting rules like no-shadow and no-var exist specifically to prevent this. Enable them in ESLint to catch accidental variable shadowing and var usage before they reach production.

Top comments (0)