JavaScript destructuring: arrays, objects, defaults, and function params

Tech reviewed: Deepak Prasad
JavaScript destructuring: arrays, objects, defaults, and function params

Destructuring assignment unpacks values from arrays or properties from objects into bindings using a compact pattern syntax introduced in ES2015. Defaults, renaming, nested patterns, and rest collectors mirror what you would otherwise spell out with repeated indexing or dot access. It works especially well alongside the spread operator in JavaScript when you clone or merge objects and arrays.

Tested on: Node.js v20.18.2. A short note after each runnable snippet describes what you should see in the console.


Quick reference

You want Pattern sketch
Positional unpack const [a, b] = arr
Skip slots const [, b] = arr
Defaults const [a = 1] = maybeShort
Collect rest const [a, ...rest] = arr
Object fields by name const { x, y } = obj
Rename const { x: ex } = obj
Defaults on props const { x = 1 } = obj
Safe function param object function f({ x } = {}) { }

Manual assignment vs destructuring

Without destructuring you index or dot into the source repeatedly:

javascript
const colors = ['red', 'green', 'blue'];
const color1 = colors[0];
const color2 = colors[1];
const color3 = colors[2];

console.log(color1, color2, color3);
Output

You should see one line: red green blue.

With array destructuring javascript syntax, the same bindings are one line:

javascript
const colors = ['red', 'green', 'blue'];
const [color1, color2, color3] = colors;

console.log(color1, color2, color3);
Output

You should see the same three color names on one line.

Nested structures work too: each position in the pattern can itself be an object or array pattern.

javascript
const data = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' },
];
const [{ name: user1 }, { name: user2 }] = data;

console.log(user1, user2);
Output

You should see one line: John Jane.


Object destructuring javascript

Bindings use property names from the right-hand object. Order of keys in the source object does not matter.

javascript
const person = {
  firstName: 'John',
  lastName: 'Doe',
  age: 30,
};

const { firstName, lastName, age } = person;

console.log(firstName);
console.log(lastName);
console.log(age);
Output

You should see three lines: John, then Doe, then 30.

Nested object pattern

Mirror the shape on the left: here name is unpacked into firstName / lastName bindings.

javascript
const person = {
  name: {
    first: 'John',
    last: 'Doe',
  },
  age: 30,
};

const {
  name: { first: firstName, last: lastName },
  age,
} = person;

console.log(firstName);
console.log(lastName);
console.log(age);
Output

You should see the same three-line output as the flat person example above.

Pick fields and collect the rest

The rest pattern in object destructuring builds a new object with the properties you did not bind. For the relationship between ... in literals and in patterns, see the spread operator guide.

javascript
const chair = {
  brand: 'Le Chair',
  legs: '4',
  material: 'Wood',
  inventory: '42',
  price: '$29.99',
  color: 'Blue',
};

const { brand, price, ...rest } = chair;

console.log(brand);
console.log(price);
console.log(JSON.stringify(rest));
Output

You should see Le Chair, then $29.99, then one JSON line with the remaining four string properties.

Rename and defaults

Rename with propertyName: variableName. Defaults use = and apply when the value is undefined.

javascript
const o = { x: 1, y: 2 };
const { x: ex, y: why } = o;
console.log(ex, why);

const { m = 10, n = 5 } = { m: 3 };
console.log(m, n);
Output

You should see 1 2 on the first line and 3 5 on the second (n used its default because it was missing).


Array destructuring javascript

Patterns use square brackets. Position determines which index supplies each binding.

javascript
const arr = [1, 2];
const [first, second] = arr;
console.log(first, second);
Output

You should see 1 2.

Skipping elements

Use an empty slot between commas—no binding for that index.

javascript
const arr = [1, 2];
const [, second] = arr;
console.log(second);
Output

You should see 2.

javascript
const arr = [1, 2, 3, 4, 5];
const [, b, , , e] = arr;
console.log(b, e);
Output

You should see 2 5.

Rest of the array

After fixed positions, ...others collects the remaining elements (see spread operator for ... in values).

javascript
const winners = ['bear', 'cat', 'dog', 'giraffe', 'unicorn'];

const [firstPlace, secondPlace, thirdPlace, ...others] = winners;

console.log(firstPlace);
console.log(secondPlace);
console.log(thirdPlace);
console.log(JSON.stringify(others));
Output

You should see three animal names on separate lines, then a JSON array with the two leftovers.

More variables than values

Missing elements are undefined unless you give a default.

javascript
const [first, second, third] = ['Homer', 'Marge'];
console.log(first, second, third);
Output

You should see Homer Marge undefined.

javascript
const [first, second, third = 'Person'] = ['Homer', 'Marge'];
console.log(first, second, third);
Output

You should see Homer Marge Person—the default filled the missing slot.

Hole skip shorthand

javascript
const [a, , b] = [1, 2, 3, 4, 5, 6, 7];
console.log(a, b);
Output

You should see 1 3 (index 1 was skipped).


Array vs object destructuring

Arrays [] Objects {}
Matching By index (position) By property name
Order Order of pattern matters Order of keys in source does not matter
Rename N/A at top level (use nested pattern) { old: fresh }
Rest ...tail is an array of leftovers ...rest is an object of leftovers

Destructuring function parameters

Parameters can use the same patterns. Here the argument must be an object with firstName and lastName.

javascript
function hello({ firstName, lastName }) {
  console.log(`Hello, ${firstName} ${lastName}!`);
}

hello({ firstName: 'Michael', lastName: 'Dell' });
Output

You should see one greeting line including Michael and Dell.

Default the whole parameter object

If callers may omit the object, default the parameter before the inner pattern runs (same idea as MDN’s “destructured parameter with default value assignment”):

javascript
function cfg({ host = 'localhost', port = 8080 } = {}) {
  return `${host}:${port}`;
}

console.log(cfg());
console.log(cfg({ host: '127.0.0.1' }));
Output

You should see localhost:8080 then 127.0.0.1:8080.


Destructuring in for...of

Each iteration value can be unpacked in the loop head—common for rows from an API.

javascript
const data = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' },
];

for (const { id, name } of data) {
  console.log(`${name} has id ${id}`);
}
Output

You should see two lines, one per row, with names and ids interpolated.


Assignment without let / const

When assigning to previously declared variables, wrap the object pattern in parentheses so { starts an expression, not a block:

javascript
let p;
let q;
({ p, q } = { p: 9, q: 8 });
console.log(p, q);
Output

You should see 9 8.


Computed keys and swap

Computed property name in the pattern:

javascript
const key = 'z';
const { [key]: v } = { z: 99 };
console.log(v);
Output

You should see 99.

Swap two variables without a temporary:

javascript
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b);
Output

You should see 2 1.


Summary

Arrays unpack by position (skip holes, gather rest, use defaults for missing slots); objects unpack by property name (rename, nest, rest for leftovers). Function params need = {} when the whole argument may be omitted.

Destructuring assignment lets you pull values out of arrays or objects without repeating const lines for every field. Array patterns care about order, holes, rest elements, and defaults; object patterns care about property names, renaming with :, nested shapes, and computed keys. A common FAQ is whether defaults run when the source is null; they only fill in for undefined, which is why optional chaining and nullish coalescing solve different problems.

When destructuring function parameters, another recurring question is what happens if the caller omits the object entirely—use a default object such as ({ a = 1 } = {}) so the binding never tries to read properties from undefined. Using the same syntax inside for...of keeps iteration code compact while staying readable for teammates who already know the object shape you are walking.


References


Frequently Asked Questions

1. What is the difference between array and object destructuring in JavaScript?

Array destructuring is positional: the first binding maps to index 0, the second to index 1, and you can skip slots with empty commas. Object destructuring matches by property name (order does not matter) and can rename with oldName: newVar.

2. When does a default value in destructuring apply?

When the unpacked value is undefined. A missing property or array element is undefined; null does not trigger the default.

3. Why do I see Cannot destructure property of undefined when destructuring function arguments?

If you write function f({ a }) { } and call f() with no argument, the binding pattern runs on undefined. Use a whole-parameter default such as function f({ a } = {}) { } or validate the argument first.

4. Why do I need parentheses for ({ a, b } = obj) without let or const?

A line starting with { is parsed as a block, not an object pattern. Wrap the assignment in parentheses: ({ a, b } = obj).

5. How do I skip array elements when destructuring?

Leave the slot empty between commas, for example const [, second] = arr or const [a, , b] = arr.

6. What does rest ...rest do in a destructuring pattern?

In arrays it collects the remaining elements into a new array. In objects it collects remaining own enumerable properties into a new object. It must be last in the pattern.

7. Can I destructure in a for-of loop?

Yes. for (const { id, name } of rows) { } unpacks each iterable element that is an object.
Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs user-friendly interfaces to enhance digital …

  • JavaScript
  • Web Design