MediumClosures & Scope🐛 Debug Challenge

Shared mutable default accumulates state

Buggy Code — Can you spot the issue?

const DEFAULT = { tags: [] };

function createPost(title, config = DEFAULT) {
  config.tags.push('published');
  return { title, tags: config.tags };
}

const p1 = createPost('Hello');
const p2 = createPost('World');
console.log(p1.tags.length);
console.log(p2.tags.length);

Fixed Code

function createPost(title, config = {}) {
  const tags = [...(config.tags ?? []), 'published'];
  return { title, tags };
}

const p1 = createPost('Hello');
const p2 = createPost('World');
console.log(p1.tags.length);
console.log(p2.tags.length);

Bug Explained

Bug: DEFAULT is a shared object. push() mutates the shared tags array. p2 starts with p1's tag already in it.

Explanation: Using {} as default and spreading creates a fresh array per call. No shared state between calls.

Key Insight: Never mutate default parameter objects. Use {} and create fresh arrays/objects inside the function body.

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