JavaScript · Intermediate

Destructuring in JavaScript

9 min readUpdated September 24, 2026Every example verified

In short: Destructuring is JavaScript syntax (ES2015) that unpacks values from an array or properties from an object into separate variables in one statement: const [a, b] = pair picks by position and const { name, age } = person picks by property name. Patterns can set defaults, rename, nest and appear in function parameters.

What destructuring does and why it exists

Programs constantly pull pieces out of containers: the third field of a comma-separated line, the name and email of a user record, the first two entries of a sorted list. Without destructuring each piece costs a line of its own, const route = parts[0]; const departs = parts[1];, and the reader has to work out what the indexes mean. Destructuring writes the shape of the container on the left of = and JavaScript fills the variables from the value on the right. The code is shorter, and every piece is named at the point where it is extracted.

Array destructuring matches by position. const [first, second] = items assigns items[0] and items[1]; extra elements are ignored and a missing one gives undefined. Leaving a gap between two commas skips a position. The right-hand side can be any iterable, so the same pattern works on strings, Map entries and the [key, value] pairs from Object.entries, which is why for (const [key, value] of Object.entries(obj)) reads so naturally.

Object destructuring matches by name. const { item, qty } = order creates variables called item and qty from the properties with those names; the order inside the braces does not matter. To store a property under a different variable name write property: newName, as in const { qty: quantity } = order. The rule to hold on to is that the name on the left of the colon is the property being looked up and the name on the right is the variable being created.

A default applies when the extracted value is undefined: const { note = "none" } = order. The default is not used for null, an empty string or 0, because those are real values that the source deliberately holds. Patterns nest: const { place: { room } } = reading reaches into an inner object and const [[a, b]] = grid into an inner array. A nested pattern creates variables only for the innermost names, so place itself is not declared.

A parameter list is also a place where variables are created, so destructuring works there too. function describe({ item, qty }) accepts one object and names its parts in the signature, which documents what the function needs and lets callers pass the properties in any order. This is the usual way to write a function that takes an options object.

Destructuring also works as a plain assignment to variables that already exist. [a, b] = [b, a] swaps two values without a temporary. Assigning into existing variables from an object needs parentheses around the whole statement, ({ x, y } = point), because a statement that starts with { would otherwise be parsed as a block. In every form destructuring only reads from the source; the array or object on the right is never modified.

Syntax

 JavaScript · syntax
const [a, b] = array;              // by position
const [first, , third] = array;    // skip a position
const [head, ...tail] = array;     // collect the rest into an array

const { name, size } = obj;        // by property name
const { name: label } = obj;       // rename: property name -> variable label
const { size = "M" } = obj;        // default when the property is undefined
const { owner: { city } } = obj;   // nested: creates only city

function f({ name, size = "M" }) { /* ... */ }   // in a parameter list
[a, b] = [b, a];                                 // assignment to existing variables
({ name, size } = obj);                          // object form needs parentheses

The ...tail form is a rest element; it and its twin, spread, are the subject of the next lesson, spread and rest.

Array destructuring: fields of a timetable line

Each input line holds a route number, a departure and an arrival separated by commas. The pattern names the three fields as soon as the line is split.

 JavaScript
let line;
while ((line = readline()) !== null) {
  const [route, departs, arrives] = line.split(",");
  console.log(`Route ${route} leaves at ${departs} and arrives at ${arrives}`);
}

const [first, , third] = ["Mon", "Tue", "Wed"];
console.log(first, third);

let a = "north";
let b = "south";
[a, b] = [b, a];
console.log(a, b);

Input given to the program: 41,07:10,07:4841,08:10,08:507,08:25,09:15

Output

Route 41 leaves at 07:10 and arrives at 07:48
Route 41 leaves at 08:10 and arrives at 08:50
Route 7 leaves at 08:25 and arrives at 09:15
Mon Wed
south north

line.split(",") returns an array and the pattern [route, departs, arrives] takes its elements in order, so the loop body never mentions an index. The empty slot in [first, , third] skips "Tue". The last three lines swap two existing variables: the array on the right is built first with the old values, then unpacked into a and b, so no temporary variable is needed.

Object destructuring with defaults, renaming and parameters

Bakery orders may or may not carry a note. The function destructures its parameter and supplies a default; the last two lines rename while extracting.

 JavaScript
const orders = [
  { item: "rye loaf", qty: 2, note: "sliced" },
  { item: "almond croissant", qty: 6 },
  { item: "sourdough", qty: 1, note: "" }
];

function describe({ item, qty, note = "no note" }) {
  return `${qty} x ${item} (${note})`;
}

for (const order of orders) {
  console.log(describe(order));
}

const { item: firstItem, qty: firstQty } = orders[0];
console.log(firstItem, firstQty);

Output

2 x rye loaf (sliced)
6 x almond croissant (no note)
1 x sourdough ()
rye loaf 2

The second order has no note property, so its value is undefined and the default "no note" steps in. The third order has note: "", an empty string, which is a real value: the default is not used and the parentheses come out empty. In { item: firstItem } the property item is read and stored in a new variable firstItem; no variable called item is created by that line.

Nested patterns and destructuring in a loop

A sensor reading holds an inner object and an array. One pattern reaches into both, and Object.entries turns an object into pairs that a loop can destructure.

 JavaScript
const reading = {
  id: "s-104",
  value: 21.5,
  place: { room: "greenhouse", shelf: 3 },
  tags: ["temp", "indoor"]
};

const { id, place: { room, shelf }, tags: [primary] } = reading;
console.log(id, room, shelf, primary);

const stock = { flour: 12, sugar: 4, yeast: 0 };
for (const [name, count] of Object.entries(stock)) {
  console.log(`${name}: ${count}`);
}

const { room: where = "unknown", floor = 1 } = reading.place;
console.log(where, floor);

Output

s-104 greenhouse 3 temp
flour: 12
sugar: 4
yeast: 0
greenhouse 1

place: { room, shelf } means "look up place, then destructure what you find"; it creates room and shelf but no variable named place. tags: [primary] mixes an object pattern with an array pattern to take the first tag. Object.entries(stock) yields [key, value] arrays, and the loop pattern names both parts. The final line combines renaming with a default: room is renamed to where and floor, which does not exist, receives 1.

Array pattern versus object pattern

QuestionArray pattern [ ]Object pattern { }
Matched bypositionproperty name
Works onany iterable: arrays, strings, Maps, Setsany object, including arrays (by index name)
Skip a valueleave a gap: [a, , c]just omit the name
Renamenot needed, you choose the name{ prop: newName }
Default[a = 1] when the element is undefined{ a = 1 } when the property is undefined
Assignment to existing vars[a, b] = arr({ a, b } = obj) with parentheses

Common mistakes

  • Destructuring a value that is undefined or null

    Why it goes wrong: const { name } = user throws a TypeError when user is undefined or null, because there is no object to read a property from. This happens often with optional function arguments and lookups that found nothing.

    Fix: Give the source a fallback object, or default the parameter itself: function f({ name } = {}). The ?? operator needs ES2020.

     JavaScript · fix
    const { name = "guest" } = user ?? {};
    function show({ name } = {}) { console.log(name); }
  • Reading the rename backwards

    Why it goes wrong: const { label: name } = obj looks up the property label and stores it in a variable called name. People often write it the other way round and then wonder why name is undefined.

    Fix: Read property: variable, left to right. If you want the property name in a variable label, write { name: label }.

  • Forgetting the parentheses when assigning into existing variables from an object

    Why it goes wrong: { x, y } = point; is a syntax error: a statement beginning with { is parsed as a block, not an object pattern.

    Fix: Wrap the whole assignment in parentheses, or declare the variables in the same statement.

     JavaScript · fix
    let x, y;
    ({ x, y } = { x: 3, y: 8 });
  • Expecting a default to replace null

    Why it goes wrong: Defaults trigger only for undefined. const { note = "none" } = { note: null } leaves note as null, because null is a present value.

    Fix: If null should also fall back, handle it after extracting: const label = note ?? "none";.

Where you use this

Destructuring shows up wherever a function needs several settings. Instead of six positional parameters that callers must remember in order, the function takes one object and destructures it with defaults in the signature; a caller can then write draw({ width: 40, colour: "teal" }) and omit everything else. The same idea gives a function a tidy way to return more than one value: return a small array or object and let the caller unpack it on the receiving side.

It is equally common in data processing. When a loop walks over records from a file or an API response, destructuring the record in the for...of head, for (const { id, total } of invoices), keeps the loop body focused on the calculation rather than on invoice.total lookups.

 JavaScript · in practice
function range(values) {
  return { lowest: Math.min(...values), highest: Math.max(...values) };
}
const { lowest, highest } = range([14, 3, 22, 9]);

Key points

  • Array patterns match by position and work on any iterable; object patterns match by property name.
  • A default (= value) is used only when the extracted value is undefined, never for null, 0 or "".
  • Rename with property: variable; the property is on the left of the colon.
  • Nested patterns create variables only for the innermost names.
  • Destructuring in a parameter list is the standard way to accept an options object.
  • Assigning into existing variables from an object needs parentheses; swapping uses [a, b] = [b, a].
  • The source array or object is only read, never changed.

Try it yourself

The input line holds a city, a low temperature and a high temperature separated by commas. Replace the three indexed lookups with one array destructuring statement so the program still prints the same line.

Your program
const parts = readline().split(",");
const city = parts[0];
const low = parts[1];
const high = parts[2];
console.log(`${city}: ${low} to ${high}`);
Input the program receives: Oslo,-3,4
Expected output: Oslo: -3 to 4

Practise this

Exercises for this lesson are in the JavaScript practice set.

Open the JavaScript playground

Frequently asked questions

Can I destructure a string in JavaScript?

Yes. Strings are iterable, so an array pattern takes their characters: const [first, second] = "hi" gives "h" and "i". An object pattern also works on a string because it is boxed to a String object, so const { length } = "hello" gives 5, though that form is rarely useful.

What happens when the array has fewer elements than the pattern?

The variables with no matching element become undefined, exactly as reading a missing index would. Add a default to any position that may be absent: const [name, role = "member"] = fields. Nothing is thrown unless the source itself is not iterable.

How do I destructure inside a for...of loop?

Put the pattern where the loop variable goes: for (const { name, qty } of orders) for objects, or for (const [key, value] of Object.entries(obj)) for key-value pairs. The pattern is applied to each element in turn, so the loop body can use the names directly.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with Node.js 22 at build time; runs in your browser in an isolated Web Worker by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.