šŸ”“ HardClosure TrapsšŸ› Debug Challenge

Default parameter function creates new closure each call

Buggy Code — Can you spot the issue?

const cache = new WeakMap();

function process(data, transform = (x) => x * 2) {
  if (cache.has(transform)) {
    console.log('cache hit');
    return cache.get(transform)(data);
  }
  console.log('cache miss');
  cache.set(transform, transform);
  return transform(data);
}

process(5); // cache miss
process(5); // cache miss again — different transform reference!
process(5); // always miss

Fixed Code

// Define the default OUTSIDE the function — same reference always
const defaultTransform = (x) => x * 2;
const cache = new WeakMap();

function process(data, transform = defaultTransform) {
  if (cache.has(transform)) {
    console.log('cache hit');
    return cache.get(transform)(data);
  }
  console.log('cache miss');
  cache.set(transform, transform);
  return transform(data);
}

process(5); // cache miss
process(5); // cache hit — same defaultTransform reference
process(5, x => x * 3); // cache miss (new function provided)

Bug Explained

Bug: Default parameter (x) => x * 2 creates a NEW function object every time process() is called without a transform. Each call has a unique reference — WeakMap never finds it.

Explanation: Default parameter expressions are evaluated fresh on each call — each evaluation creates a new function object with a new reference. Moving the default outside ensures a stable reference across calls.

Key Insight: Default parameter expressions are re-evaluated on every call. If identity (===) matters (for caching, WeakMap keys, memoization), define constants outside the function.

More Closure Traps Debug Challenges

🟢 Easyvar in loop — buttons all say last valueā†’šŸŸ” MediumStale closure in React useEffectā†’šŸ”“ HardMemoization closure caching wrong scopeā†’šŸŸ” MediumPrivate variable accidentally exposed→

Practice spotting bugs live →

38 debug challenges with AI hints

šŸ› Try Debug Lab