JavaScript · Intermediate
Destructuring in JavaScript
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
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 parenthesesThe ...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.
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:48 ↵ 41,08:10,08:50 ↵ 7,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.
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.
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
| Question | Array pattern [ ] | Object pattern { } |
|---|---|---|
| Matched by | position | property name |
| Works on | any iterable: arrays, strings, Maps, Sets | any object, including arrays (by index name) |
| Skip a value | leave a gap: [a, , c] | just omit the name |
| Rename | not 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 } = userthrows a TypeError whenuserisundefinedornull, 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 · fixconst { name = "guest" } = user ?? {}; function show({ name } = {}) { console.log(name); }Reading the rename backwards
Why it goes wrong:
const { label: name } = objlooks up the propertylabeland stores it in a variable calledname. People often write it the other way round and then wonder whynameisundefined.Fix: Read
property: variable, left to right. If you want the propertynamein a variablelabel, 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 · fixlet 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 }leavesnoteasnull, becausenullis a present value.Fix: If
nullshould 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.
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 isundefined, never fornull,0or"". - 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.
const parts = readline().split(",");
const city = parts[0];
const low = parts[1];
const high = parts[2];
console.log(`${city}: ${low} to ${high}`);Oslo,-3,4Oslo: -3 to 4const [city, low, high] = readline().split(",");
console.log(`${city}: ${low} to ${high}`);Practise this
Exercises for this lesson are in the JavaScript practice set.
Open the JavaScript playground
Related lessons
- JavaScript Arrays: Creating, Indexing and Changing ListsHow JavaScript arrays store ordered lists: indexing, length, push and pop, slice versus splice, sorting numbers correctly, and map, filter and reduce.10 min
- JavaScript Objects: Properties, Methods and NestingHow JavaScript objects group data as key-value properties: dot and bracket access, adding and deleting, methods with this, nesting and reference behaviour.10 min
- Spread and Rest in JavaScriptWhat the ... operator does in JavaScript: spreading arrays and objects into calls and literals, and collecting rest parameters and rest elements.9 min
- JavaScript Functions: Declarations, Arrows and Return ValuesHow to define and call JavaScript functions with declarations, expressions and arrow functions; parameters, default values, return and local scope.10 min
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.
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.