Destructuring, spread and rest

JavaScript · Practical · 12 min

Modern APIs, React props and function signatures are written with these patterns everywhere.

Destructuring pulls values out of objects and arrays into variables, mirroring literal syntax:

const { name, age } = user;          // by property name
const [first, second] = items;       // by position

Object patterns can rename ({ name: userName }), give defaults ({ role = "guest" }) and nest ({ address: { city } }). Array patterns can skip elements ([, second]) and swap variables: [a, b] = [b, a]. Defaults apply only when the value is undefined, not null.

Destructuring works in function parameters, which gives you named, optional arguments:

function connect({ host = "localhost", port = 5432 } = {}) { ... }

The trailing = {} lets callers omit the argument entirely.

The ... syntax means two opposite things depending on where it appears:

  • Rest (in a pattern or parameter list) collects the remaining items: const { password, ...safeUser } = user; and function log(level, ...messages).
  • Spread (in a literal or call) expands an iterable or object: [...a, ...b], { ...defaults, ...overrides }, Math.max(...nums).

With object spread, later properties win, which makes it the standard way to merge settings or produce an updated copy: { ...todo, done: true }.

Spread copies are shallow: nested objects are still shared between the copy and the original. That matters when you update state immutably — copy each level you change.

Example

const user = { id: 1, name: "Ada", password: "s3cret", address: { city: "London" } };

const { name, address: { city }, role = "guest" } = user;
console.log(name, city, role);          // Ada London guest

const { password, ...safeUser } = user; // rest: everything else
console.log(Object.keys(safeUser));     // [ 'id', 'name', 'address' ]

let [a, b] = [1, 2];
[a, b] = [b, a];                        // swap
console.log(a, b);                      // 2 1

function connect({ host = "localhost", port = 5432 } = {}) {
  return `${host}:${port}`;
}
console.log(connect({ port: 6543 }));   // localhost:6543
console.log(connect());                 // localhost:5432

const defaults = { theme: "light", fontSize: 14 };
const settings = { ...defaults, theme: "dark" };
console.log(settings);                  // { theme: 'dark', fontSize: 14 }
console.log(Math.max(...[3, 9, 4]));    // 9

Remember

  • Destructure objects by name and arrays by position, with defaults and renames
  • ... in a pattern collects (rest); in a literal or call it expands (spread)
  • Spread copies are shallow; later properties override earlier ones

Watch out for

  • Destructuring a parameter without = {} and then calling the function with no argument (TypeError).

Check yourself

  1. const { a = 1 } = { a: null }; — what is a?
  2. After const copy = { ...orig }; copy.nested.x = 5; does orig.nested.x change?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring