Destructuring
Learn how JavaScript destructuring unpacks arrays and objects into variables, with defaults, renaming, nested patterns, parameters, swaps, and the evaluation rules that prevent surprises.
- 01Unpack arrays and objectsChoose values by position or by property name.
- 02Use patterns safelyApply defaults, renaming, nested patterns, and parameter defaults without crashes.
- 03Predict edge casesExplain when defaults run, why null behaves differently, and why object assignment needs parentheses.
Unpack in one move
Destructuring is JavaScript syntax for unpacking values from arrays and objects directly into variables. Instead of opening a value, reading one piece, then writing another line for the next piece, you write a pattern that has the same shape as the data you want.
Destructuring means: put a pattern on the left of =, put an iterable or object on the right, and JavaScript binds the named pieces for you.
Imagine a delivery box arriving at your desk. Destructuring lets you put the mug into the mug drawer and the notebook into the notes drawer in one motion. The pattern says which drawer each item goes to.
- In real life: Items in the box
- In JavaScript: Values inside an array or object
- In real life: Drawer labels
- In JavaScript: Variable names in the pattern
- In real life: Missing item gets a spare
- In JavaScript: A default value such as
role = "guest" - In real life: A drawer with a different label
- In JavaScript: Renaming:
{ name: fullName }
Where the analogy stops: A real box is opened by a person who can improvise. JavaScript follows exact rules: arrays are unpacked by iteration order, objects by property name, and null or undefined cannot be opened as objects.
This lesson uses the curriculum summary for this topic: unpack arrays and objects into variables. You will also see defaults, renaming, nesting, parameters, swaps, and the exact evaluation rules professionals rely on.
Array destructuring
BY POSITIONArray destructuring unpacks by position, like seats in a row. The first pattern slot receives the first iterated value, the second slot receives the second, and so on. Arrays are the common case, but the later Iteration protocols lesson will explain why any iterable can work.
const scores = [98, 87, 91];
const [first, second] = scores;
const [winner, , bronze = 0] = scores;
console.log(first, second, bronze); // 98 87 91The empty space between two commas skips the second value. A default, like bronze = 0, only fills in when the value for that slot is undefined. The ...tail rest pattern gathers remaining values; that gets a full treatment in the next lesson, Spread & rest.
If three friends sit in a row, you can point to the first chair and the third chair without asking their names. Array destructuring does the same thing with positions.
- In real life: Seat 1, seat 2, seat 3
- In JavaScript: Array positions 0, 1, 2
- In real life: Leave seat 2 empty
- In JavaScript: A skipped slot:
[first, , third] - In real life: Everyone after the first follows you
- In JavaScript: A rest pattern:
[head, ...tail]
Where the analogy stops: Seat numbers are printed on tickets. Iterables produce values one after another; array indexes are only one common way to think about that order.
Object destructuring
BY NAMEObject destructuring unpacks by property name. The order of properties in the object literal is not the point; the pattern asks for names. It is like picking your own mail out of mailboxes labeled by recipient.
const user = { id: 7, name: "Mina", role: "admin" };
const { name, role } = user;
console.log(name, role); // Mina adminIf the variable should have a different name, put the property name before a colon and the new variable name after it: { name: fullName }. The colon does not mean “type”; it means “read this property into that variable.”
Defaults and renaming
INTERACTIVEDefaults are your spare items: if the box is missing this item, use the spare. Renaming is putting an item into a drawer with a different label. Try both in the lab.
const user = { name: "Ada Lovelace", age: 36, address: { city: "Pune" }};const scores = [98, 87, 91, 76]; // Choose one pattern in the lab controls. const { name, age } = user;"Ada Lovelace"36Object destructuring looks for properties named name and age, no matter where they appear in the object literal.
user object and scores array.| Pattern | Meaning |
|---|---|
| const [first, second] = list; | Take items by position. |
| const [first, , third] = list; | Skip a position with an empty slot. |
| const { name } = user; | Take a property by name. |
| const { name: fullName } = user; | Read name, create fullName. |
| const { role = 'guest' } = user; | Use a default only when the read value is undefined. |
| const { address: { city } } = user; | Go inside address and bind city; address itself is not created. |
Nested patterns
SHAPESA pattern can go inside another pattern. That lets you reach into objects inside objects, arrays inside arrays, or the mix you often get from an API response.
const user = { address: { city: "Pune" } };
const { address: { city } } = user;
console.log(city);
console.log(address);Line 2 creates city. It does not create a variable named address; address: is the path to walk through. Reading address on line 4 throws a ReferenceError because no such variable was declared.
const response = { data: [{ id: 1, title: "Destructuring" }] };
const { data: [firstArticle] } = response;
const { id, title } = firstArticle;Destructuring parameters
OPTIONSFunction parameters can be patterns too. This is common for options objects and React-style props: the caller sends one object, and the function names the pieces it needs.
function greet({ name, greeting = "Hello" } = {}) {
return `${greeting}, ${name ?? "friend"}`;
}
console.log(greet({ name: "Mina" }));
console.log(greet());The = after the parameter is important. Without it, calling greet() would try to destructure undefined and throw a TypeError. The inner greeting = "Hello" is a different default: it fills one property after the outer object exists.
Swapping values
STEP THROUGHAssignment destructuring can write into existing variables. The classic trick is swapping two values without making a temporary variable yourself.
Swapping works because the whole right side is read before the left side is written.
script
let right = "coffee";[left, right] = [right, left];console.log(left, right);Object assignment destructuring needs parentheses: ({ x, y } = point);. Without them, a leading { starts a block statement. Also, if the previous line omits a semicolon, a line starting with [a, b] can be parsed as continuing the previous expression; the Statements, comments & semicolons lesson explains automatic semicolon insertion.
How destructuring evaluates
STEP THROUGHDestructuring is short, but it is not magic. JavaScript evaluates the right side first, then walks the pattern from left to right. Defaults run only when the value being assigned is undefined, and they are lazy: a default expression is not evaluated unless it is needed.
Predict whether spare() will run. Step through the real destructuring assignment to check.
script
function spare() { calls = calls + 1; return "spare";}const source = { item: undefined };const { item = spare() } = source;console.log(item, calls);const { a = 1 } = { a: undefined };const { a = 1 } = { a: null };const { a = 1 } = {};const { a } = null;const [a = 1] = [];const [a = 1] = [0];
Sort each pattern by whether it keeps the source value, uses the default, or throws before binding.
Destructuring from null or undefined throws a TypeError for object patterns because there is no object to read. Array patterns need an iterable; null and undefined are not iterable either.
Where you will use this
Destructuring makes data-heavy code easier to scan. You will use it when reading settings, unpacking responses, naming entries from Object.entries, returning multiple values, and accepting option objects.
const totals = { apples: 3, oranges: 2 };
for (const [fruit, count] of Object.entries(totals)) {
console.log(fruit + ": " + count);
}let x;
let y;
const point = { x: 4, y: 9 };
({ x, y } = point);
console.log(x, y);Common misconceptions
- “Defaults replace any falsy value.” They replace only
undefined, notnull,0,false, or an empty string. - “Renaming creates both names.”
{ name: fullName }createsfullName, notname. - “Nested paths become variables.”
{ address: { city } }createscity;addressis only the path. - “Object assignment can start a statement by itself.” Existing variables need
({ x } = point);. - “Array destructuring is only for arrays.” It works with iterables. Arrays are just the friendly starting point.
Practice exercises
5 EXERCISESRead the program and predict the printed number.
const scores = [10, 20, 30];
const [first, , third = 99] = scores;
console.log(first + third);const scores = [10, 20, 30];
const [first, , third = 99] = scores;
console.log(first + third);first is 10. The pattern skips 20; third receives 30, so the default 99 is not used. The sum is 40.
Use nested object destructuring, renaming, and a default to print the user id, name, and role.
const response = { user: { id: 7, name: "Mina" } };
const { user: { id: userId, name, role = "reader" } } = response;
console.log(userId, name, role);const response = { user: { id: 7, name: "Mina" } };
const { user: { id: userId, name, role = "reader" } } = response;
console.log(userId, name, role);The nested pattern enters user, renames id to userId, keeps name, and uses the default reader because role is missing.
Swap a and b with one destructuring assignment.
let a = "left";
let b = "right";
[a, b] = [b, a];
console.log(a, b);let a = "left";
let b = "right";
[a, b] = [b, a];
console.log(a, b);JavaScript evaluates [b, a] first, then assigns its two saved values back into [a, b].
Add the missing parameter default so calling the function with no argument works.
function label({ text = "Untitled" }) {
return text.toUpperCase();
}
console.log(label());function label({ text = "Untitled" } = {}) {
return text.toUpperCase();
}
console.log(label());= {} gives the parameter something safe to destructure when no argument is passed. Then text = "Untitled" can run.
Use the entries pair pattern in a loop and predict the first line.
const counts = { apples: 3, oranges: 2 };
for (const [fruit, count] of Object.entries(counts)) {
console.log(fruit + "=" + count);
}const counts = { apples: 3, oranges: 2 };
for (const [fruit, count] of Object.entries(counts)) {
console.log(fruit + "=" + count);
}Object.entries(counts) produces pairs like ["apples", 3]. The loop header destructures each pair into fruit and count.
Check your understanding
7 QUESTIONSQuestion 1 of 7Array destructuring chooses values by what?
Choose an answer to see the explanation.
Question 2 of 7What does the skipped array slot snippet print?
Read the code, then predictconst [first, , third = 9] = [4, 5]; console.log(first, third);Choose an answer to see the explanation.
Question 3 of 7What variable is created by
const { name: fullName } = user;?Choose an answer to see the explanation.
Question 4 of 7What does the null default snippet print?
Read the code, then predictconst { a = "spare" } = { a: null }; console.log(a);Choose an answer to see the explanation.
Question 5 of 7Why does
function greet({ name } = {})include= {}?Choose an answer to see the explanation.
Question 6 of 7What does the swap snippet print?
Read the code, then predictlet x = "a"; let y = "b"; [x, y] = [y, x]; console.log(x + y);Choose an answer to see the explanation.
Question 7 of 7Which assignment destructuring form is valid for existing variables?
Choose an answer to see the explanation.
Key takeaways
- Array patterns unpack by iteration position; object patterns unpack by property name.
- Defaults run only for
undefinedand only when needed. - Renaming changes the variable name, not the source property.
- Nested patterns walk through data; path labels are not automatically variables.
- Use
=for parameter destructuring that should tolerate missing arguments.
Destructuring is pattern matching for assignment: the left side describes the pieces JavaScript should pull from the right side.
Up next: Spread & rest.