Destructuring reads several properties into local variables in one expression, and defaults fill in missing values. Two details decide correctness: a default applies only when the value is undefined, and a nested pattern throws unless you give its parent a default.
Before you start
You should be comfortable with object literals, property access, and the difference between undefined and null. Every example runs in Node.js or a browser console.
Step-by-step walkthrough
Step 1: Default on missing or undefined
const { pageSize = 20 } = options binds 20 when pageSize is undefined or absent. It does not apply when the property is null, 0, '' or false, because those are real values. Choose the default operator that matches your intent instead of assuming null is treated as missing.
Step 2: Rename to avoid collisions
Use const { id: userId } = row when id would shadow another binding or read ambiguously. Renaming keeps the source property and the local name decoupled, which matters inside functions that already have an id in scope.
Step 3: Guard nested patterns
const { user: { name } } = payload throws if payload.user is missing. Give the parent a default: const { user: { name } = {} } = payload. The inner default protects the destructure even when the whole object is undefined if you also default the top level.
Worked scenario
The parameter defaults to {} so the function is callable with no arguments, and the nested option is guarded.
function connect({ host = 'localhost', port = 5432, opts: { ssl = false } = {} } = {}) {
return { host, port, ssl };
}
console.log(connect()); // { host: 'localhost', port: 5432, ssl: false }
console.log(connect({ port: 0 })); // { host: 'localhost', port: 0, ssl: false }Walk through the example
Calling connect() with no argument uses the top-level {} default, so the body receives an object. port: 0 is a defined value, so the 5432 default is skipped — a common source of “why did my zero disappear?” bugs in code that used ||. The nested opts default keeps a missing opts from throwing.
Common mistake
Expecting a default to replace null. const { name = 'anon' } = { name: null } keeps null, which then fails a later name.toUpperCase(). Normalize null explicitly, or use name ?? 'anon' when you truly want both null and undefined treated as missing.
Verify the behavior
Call the function with undefined, null, an empty object, and a partially filled object. Assert that 0 and '' survive and that only undefined triggers defaults. Add a case where the nested parent is missing and confirm the guarded pattern does not throw.
Interview exercise
You receive { data: null } and destructure { data: { items } }. What happens, and how do you fix it?
Answer and reasoning
The nested destructure throws because data is null, and a default on items cannot help: the failure happens one level up, when reading items off null. Fix it by defaulting the parent, const { data: { items } = {} } = payload, or by reading payload.data?.items and falling back afterward. The key is that defaults protect only the exact level they are written on.
Continue learning
Compare this with nullish defaults and read the MDN destructuring reference. Try the JavaScript interview questions.