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);
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
}
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
vardeclaration for a variable they intended to reference from an outer scope - Refactor code by moving variable declarations around without realizing
varhoists to the function boundary - Copy-paste code fragments that introduce an unintended local declaration
🔍 Line-by-line explanation
let count = 0;— Declarescountin the outer (module/script) scope with value0.function increment() {— JavaScript parses the entire function body before executing. It findsvar count;and hoists it to the top of the function, initializing it toundefined. This localcountshadows the outercount.count++;— Operates on the localcount, which isundefined. The++postfix operator convertsundefinedtoNaNviaNumber(undefined), then attemptsNaN + 1, which isNaN. The localcountis nowNaN.var count;— This is a no-op at runtime. The declaration was already hoisted; no new assignment happens here.return count;— Returns the localcount, which isNaN.console.log(increment());— PrintsNaN.console.log(count);— Prints0. The outercountwas never modified because the function only ever operated on its own localcount.
🔹 Key Takeaways
vardeclarations are hoisted to the top of their enclosing function — not to the line where they appear. This means avaranywhere in a function creates a local variable that exists from the very first line of that function.Hoisted
varvariables shadow outer variables silently. Unlikelet/constwhich would cause a clear TDZ error if accessed before declaration,varquietly creates anundefinedbinding that masks the outer scope.undefined++evaluates toNaN, not an error. JavaScript's type coercion convertsundefinedtoNaNbefore the arithmetic operation, producing a silent wrong result rather than a crash.Always prefer
let/constovervar. Withletorconst, attempting to use the variable before its declaration throws aReferenceError(Temporal Dead Zone), making this class of bugs immediately visible instead of silently corrupting data.Linting rules like
no-shadowandno-varexist specifically to prevent this. Enable them in ESLint to catch accidental variable shadowing andvarusage before they reach production.
Top comments (0)