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:
const colors = ['red', 'green', 'blue'];
const color1 = colors[0];
const color2 = colors[1];
const color3 = colors[2];
console.log(color1, color2, color3);You should see one line: red green blue.
With array destructuring javascript syntax, the same bindings are one line:
const colors = ['red', 'green', 'blue'];
const [color1, color2, color3] = colors;
console.log(color1, color2, color3);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.
const data = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
const [{ name: user1 }, { name: user2 }] = data;
console.log(user1, user2);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.
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30,
};
const { firstName, lastName, age } = person;
console.log(firstName);
console.log(lastName);
console.log(age);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.
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);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.
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));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.
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);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.
const arr = [1, 2];
const [first, second] = arr;
console.log(first, second);You should see 1 2.
Skipping elements
Use an empty slot between commas—no binding for that index.
const arr = [1, 2];
const [, second] = arr;
console.log(second);You should see 2.
const arr = [1, 2, 3, 4, 5];
const [, b, , , e] = arr;
console.log(b, e);You should see 2 5.
Rest of the array
After fixed positions, ...others collects the remaining elements (see spread operator for ... in values).
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));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.
const [first, second, third] = ['Homer', 'Marge'];
console.log(first, second, third);You should see Homer Marge undefined.
const [first, second, third = 'Person'] = ['Homer', 'Marge'];
console.log(first, second, third);You should see Homer Marge Person—the default filled the missing slot.
Hole skip shorthand
const [a, , b] = [1, 2, 3, 4, 5, 6, 7];
console.log(a, b);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.
function hello({ firstName, lastName }) {
console.log(`Hello, ${firstName} ${lastName}!`);
}
hello({ firstName: 'Michael', lastName: 'Dell' });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”):
function cfg({ host = 'localhost', port = 8080 } = {}) {
return `${host}:${port}`;
}
console.log(cfg());
console.log(cfg({ host: '127.0.0.1' }));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.
const data = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
for (const { id, name } of data) {
console.log(`${name} has id ${id}`);
}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:
let p;
let q;
({ p, q } = { p: 9, q: 8 });
console.log(p, q);You should see 9 8.
Computed keys and swap
Computed property name in the pattern:
const key = 'z';
const { [key]: v } = { z: 99 };
console.log(v);You should see 99.
Swap two variables without a temporary:
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b);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.
