HardClosures & Scope🐛 Debug Challenge

Default parameter creates new function every 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('hit');
    return cache.get(transform)(data);
  }
  console.log('miss');
  cache.set(transform, transform);
  return transform(data);
}

process(5);
process(5);

Fixed Code

const defaultTransform = x => x * 2;
const cache = new WeakMap();

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

process(5);
process(5);

Bug Explained

Bug: The default x => x * 2 is a NEW function object on every call. Each invocation gets a unique reference — WeakMap.has() never finds it.

Explanation: Defining the default outside gives it a stable identity. Same reference every time, so WeakMap finds it on the second call.

Key Insight: Default parameter expressions are re-evaluated on every call. If identity matters (WeakMap, memoization), define the constant outside.

More Closures & Scope Debug Challenges

Easyvar in loop — all closures share one variableMediumCounter factory shares global stateMediumShared memoize cache across all functionsEasyWallet exposes stale primitive snapshot

Practice spotting bugs live →

38 debug challenges with AI hints

🐛 Try Debug Lab