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 objectsTake values out of an array by position, and out of an object by property name.
- 02Use patterns safelyUse defaults, renaming, patterns inside patterns, and parameter defaults without errors.
- 03Predict tricky casesSay when a default is used, why
nullkeeps its value, and why unpacking into existing variables needs round brackets.
Unpack many values in one line
Destructuring is a short way to take values out of an array or an object and put them into variables. Without it, you write one line for each value you want. With it, you write one pattern. A pattern is a small sketch of the data, with a variable name in each spot you want filled.
Put a pattern on the left of =. Put an array or an object on the right. JavaScript reads the right side and puts each value into the variable at the matching spot in the pattern.
const order = ["tea", 3];
const [drink, quantity] = order;
console.log(drink, quantity);Line 1 makes an array with two values: "tea" and 3. Line 2 has the pattern [drink, quantity]. It has the same shape as the array: two spots in a row. So drink gets the first value, "tea", and quantity gets the second value, 3. Line 3 prints tea 3.
[drink, quantity] has two spots, like the array. The first value goes into the first variable. The second value goes into the second variable.A delivery box arrives at your desk. Inside are a mug and a notebook. You put the mug in the drawer labeled mug. You put the notebook in the drawer labeled notes. The pattern is your list of drawer labels. It says where each item goes.
- In real life: The items in the box
- In JavaScript: The values in the array or object
- In real life: The labels on your drawers
- In JavaScript: The variable names in the pattern
- In real life: A spare item, used only when one is missing
- In JavaScript: A default value, like
role = "guest" - In real life: Putting an item in a drawer with a different label
- In JavaScript: Renaming, like
{ name: fullName }
This lesson starts with arrays and objects. Then it shows defaults, renaming, patterns inside patterns, function parameters, and swapping two variables. It ends with the exact rules for when a default is used.
Array destructuring
BY POSITIONAn array pattern takes values by position. Position means the place in the row: first, second, third. The first name in the pattern gets the first value. The second name gets the second value, and so on.
const scores = [98, 87, 91];
const [first, second] = scores;
const [winner, , bronze = 0] = scores;
console.log(first, second, bronze); // 98 87 91Line 2 puts 98 in first and 87 in second. Line 3 shows two new things:
- The empty spot between two commas skips a value. Here it skips
87, so no variable gets it. bronze = 0is a default value: a spare value for when nothing is there. The third value,91, is there. Sobronzegets91, and the default is not used.
const cart = ["book"];
const [first, second = "pen"] = cart;
console.log(first, second);This cart has only one item. Line 2 puts "book" in first. There is no second item, so the value for that spot is undefined. A default is used only when the value is undefined. So second gets the default, "pen", and line 3 prints book pen.
You may also see [head, ...tail]. The three dots make a rest pattern: tail gets all the values that are left, in a new array. The next lesson, Spread & rest, covers it fully.
Three friends sit in a row. You can say "the person in seat 1" and "the person in seat 3" without knowing their names. You pick people by their seat number, not by their name.
- In real life: Seat 1, seat 2, seat 3
- In JavaScript: Array positions 0, 1, 2 (JavaScript counts from 0)
- In real life: Skip the person in seat 2
- In JavaScript: An empty spot:
[first, , third] - In real life: Everyone after seat 1, as one group
- In JavaScript: A rest pattern:
[head, ...tail]
const word = "OK";
const [first, second] = word;
console.log(first, second);A string can hand out its letters one at a time. So the same kind of pattern works on it. first gets "O", second gets "K", and line 3 prints O K.
A value that can hand out its items one by one, in order, is called an iterable. Arrays and strings are both iterables. An array pattern works with any iterable. The later lesson Iteration protocols explains how.
Object destructuring
BY NAMEAn object pattern takes values by property name. A property is a named value inside an object, like name: "Mina". The pattern lists the names you want, and JavaScript finds each one in the object. The order of the properties in the object does not matter. It is like picking your own letters out of a row of mailboxes, where each mailbox has a person's name on it.
const user = { id: 7, name: "Mina", role: "admin" };
const { name, role } = user;
console.log(name, role); // Mina adminLine 2 asks for name and role. It creates two variables with those same names. id is not in the pattern, so it is left alone. Line 3 prints Mina admin.
const user = { role: "reader", name: "Asha" };
const { name, role } = user;
console.log(name, role);Here the object lists role first and name second. The pattern lists them the other way round. It still works, because JavaScript looks up each one by its name. Line 3 prints Asha reader.
Sometimes you want the variable to have a different name from the property. Write the property name, a colon, and then the new variable name: { name: fullName }. Read it as "take the property name and put it in a new variable called fullName." Here the colon does not mean a type. It only means "put this property into that variable".
Defaults and renaming
INTERACTIVEA default is a spare value: if the value is missing, the variable gets the spare. Renaming gives the variable a different name from the property. You can use both in one pattern.
const profile = { name: "Asha" };
const { name: displayName, role = "reader" } = profile;
console.log(displayName, role);Line 1 makes a profile object with only a name. Line 2 does two things:
name: displayNametakes thenameproperty and puts its value in a new variable,displayName.role = "reader"asks forrole. The object has norole, so the variablerolegets the default,"reader".
Line 3 prints Asha reader.
name: displayName reads the property name and puts its value in a new variable, displayName. The object still calls it name.Try both ideas in the lab below. Each button shows a different pattern on the same data. The right side lists the variables that pattern creates.
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"36The pattern asks for the properties name and age. JavaScript finds each one by its name. Where it sits in the object does not matter.
user object and scores array.The table lists the patterns you will write most often. Read each row as: "when I write this pattern, this is what I get."
| When you write | What you get |
|---|---|
const [first, second] = list; | Takes the first two items, by position. |
const [first, , third] = list; | The empty spot skips the second item. |
const { name } = user; | Takes the name property into a variable called name. |
const { name: fullName } = user; | Takes the name property into a new variable called fullName. |
const { role = 'guest' } = user; | Takes role, or 'guest' when the value is undefined. |
const { address: { city } } = user; | Goes into address and takes city. No address variable is made. |
Nested patterns
SHAPESA pattern can sit inside another pattern. This is called a nested pattern. It lets you reach into an object that is inside another object, or an array that is inside an object. Data from a server often has this shape.
const user = { address: { city: "Pune" } };
const { address: { city } } = user;
console.log(city);
console.log(address);Line 1 makes user. Its address property holds another object, with a city inside. Line 2 reads like this: "go into address, then take city." Line 3 prints Pune.
Line 2 creates only one variable: city. The address: part is a path. It tells JavaScript where to go, but it does not create a variable called address. So line 4 throws a ReferenceError. That is the error JavaScript gives when you use a variable name that does not exist.
address: only shows the way in, like a sign. The one variable the pattern makes is city, which holds "Pune".const response = { data: [{ id: 1, title: "Destructuring" }] };
const { data: [firstArticle] } = response;
const { id, title } = firstArticle;This example mixes the two kinds of pattern. data holds an array. On line 2, the pattern { data: [firstArticle] } goes into data and takes its first item. Line 3 then unpacks that item in a second, separate step.
const response = { user: { name: "Asha", city: "Delhi" } };
const { user: { name, city } } = response;
console.log(name, city);Here the pattern goes into response.user and takes two properties at once. Line 3 prints Asha Delhi. Destructuring only reads values. It does not make a copy of the user object.
Patterns in function parameters
OPTIONSA function's parameters can be patterns too. A parameter is a name in the round brackets of a function, like name in function greet(name). Patterns here are common for an options object: the caller passes one object full of settings, and the function picks out the settings it needs. React components use the same idea for their props.
function greet({ name, greeting = "Hello" } = {}) {
return `${greeting}, ${name ?? "friend"}`;
}
console.log(greet({ name: "Mina" }));
console.log(greet());Line 1 has two defaults. Each one does a different job:
- The
= {}at the end is a default for the whole parameter. If the caller passes nothing, the parameter becomes an empty object,{}. Without it,greet()would try to unpackundefinedand throw aTypeError. That is the error JavaScript gives when a value is the wrong kind for what you asked it to do. greeting = "Hello"is a default for one property. It fillsgreetingwhen the object has nogreetingproperty.
Line 2 uses ??, which picks "friend" when name is undefined or null. Line 4 prints Hello, Mina. Line 5 passes nothing and prints Hello, friend.
function showName({ name }) {
return name;
}
console.log(showName({ name: "Asha", role: "reader" }));A pattern only needs the properties the function uses. This object also has role, but the pattern names only name. The extra property is ignored, and line 4 prints Asha.
Swapping values
STEP THROUGHDestructuring can also put values into variables that already exist. You leave out const and let. This is called destructuring assignment. A common use is a swap: each of two variables gets the other's value. You don't need a third variable to hold one value while you swap.
The swap works because JavaScript reads the whole right side before it changes the variables on the left.
script
let right = "coffee";[left, right] = [right, left];console.log(left, right);Two mistakes stop this kind of line from working:
- An object pattern on existing variables needs round brackets around the whole line:
({ x, y } = point);. Without them, JavaScript reads a{at the start of a line as the start of a block of code, not a pattern. You get aSyntaxError. - A line that starts with
[a, b]can join onto the line above it if that line has no semicolon. JavaScript then reads the two lines as one, and the swap breaks. End the line above with a semicolon. The lesson Statements, comments & semicolons explains when JavaScript adds semicolons for you.
The order JavaScript follows
STEP THROUGHDestructuring is short, but it follows fixed rules. Knowing them lets you predict every result:
- JavaScript works out the right side of
=first. - Then it goes through the pattern from left to right.
- A default is used only when the value is
undefined. - A default is not even worked out unless it is needed. If the default is a function call, the function runs only when the value is
undefined.
{ item = "spare" }, on two objects. On the left the value is undefined, so the default is used. On the right the value is null, which is a real value, so it is kept.The step-through below checks the last rule. The function spare() adds 1 to a counter, calls, each time it runs. Change the value on line 6, then watch whether the counter goes up.
Guess first: will spare() run? Then step through the code 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 line by what the variable gets: the value from the source, the default, or an error before any variable is made.
Unpacking null or undefined with an object pattern throws a TypeError, because there is no object to read properties from. An array pattern needs an iterable. null and undefined are not iterables either, so that throws too.
Where you will use this
Destructuring makes code that handles a lot of data easier to read. You will use it to:
- read settings from an options object,
- take fields out of data from a server,
- name the key and the value from
Object.entries, - return two values from a function,
- swap two variables.
It works best when you already know the shape of the data. Unpack the fields near the top of the function. Then the rest of the function can use short, clear names.
const totals = { apples: 3, oranges: 2 };
for (const [fruit, count] of Object.entries(totals)) {
console.log(fruit + ": " + count);
}Object.entries(totals) gives a list of pairs, like ["apples", 3]. Each pair is an array: the key first, then the value. On line 2, the pattern [fruit, count] unpacks each pair. The loop prints apples: 3, then oranges: 2.
let x;
let y;
const point = { x: 4, y: 9 };
({ x, y } = point);
console.log(x, y);Lines 1 and 2 make x and y with no value yet. Line 4 fills them from point. The round brackets around line 4 are needed, as the "Two syntax traps" box above explains. Line 5 prints 4 9.
function getCartSummary() {
return ["tea", 3];
}
const [drink, quantity] = getCartSummary();
console.log(drink + " x " + quantity);A function can return a small array when two values belong together. On line 4, the caller names the two positions right away. Line 5 prints tea x 3. If you return more values, or the list may grow, an object with named properties is easier to read.
Destructuring is optional. Plain user.profile.name is often better:
- when you read the value only once,
- when the object might be missing,
- when a long pattern would hide where the value comes from.
Use a pattern when you use several fields together and you know the shape of the data.
Common misconceptions
- "A default replaces any empty-looking value." It replaces only
undefined.null,0,false, and the empty string""are kept. - "Renaming creates both names."
{ name: fullName }creates onlyfullName. There is nonamevariable. - "The path in a nested pattern becomes a variable."
{ address: { city } }creates onlycity.addressis just the way in. - "Unpacking an object into existing variables can start a line on its own." It needs round brackets around the whole line:
({ x } = point);. - "Array patterns only work on arrays." They work on any iterable, such as a string. Arrays are just the most common.
- "Destructuring makes a copy." It puts the same values into variables. An object inside the data stays the same object. It is not copied.
- "You must name every property." A pattern can name only the properties you need. The other properties are ignored.
- "A longer pattern is always clearer." For a long path, unpack one level first. Then unpack the next object on its own line.
Destructuring does not protect a value from later changes. Say settings.theme holds an object, and you unpack it into a variable called theme. Now theme and settings.theme lead to the same object. If you change that object through one of them, you see the change through the other one too. If you need a separate copy, you have to make it yourself.
When a value might be missing, pick the tool that fits:
- A default handles a missing property, like
{ role = "guest" }. - Optional chaining,
?., handles a missing object along the way, likeuser.profile?.name.
They solve different problems. Check the shape of your data first. Don't hide a missing part behind a default.
Practice exercises
5 EXERCISESRead the program. What number does it print?
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 gets 10. The empty spot skips 20. third gets 30, so its default, 99, is not used. 10 + 30 is 40.
Use a nested pattern, a rename, and a default to print the user's 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 pattern goes into user. It puts id in userId, takes name as it is, and gives role 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 works out [b, a] first, so it has both old values saved. Then it puts them into a and b, in swapped order.
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 an empty object when no argument is passed. Now the pattern has an object to read, and text = "Untitled" fills in the text.
Use a pair pattern in the loop. What is the first line it prints?
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) gives pairs like ["apples", 3]. The loop line unpacks each pair into fruit and count.
Check your understanding
7 QUESTIONSQuestion 1 of 7How does an array pattern choose which value each variable gets?
Choose an answer to see the explanation.
Question 2 of 7This pattern skips a spot and has a default. What does it 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 7Which variable does
const { name: fullName } = user;create?Choose an answer to see the explanation.
Question 4 of 7The value is
nulland the pattern has a default. What does it 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 } = {})end with= {}?Choose an answer to see the explanation.
Question 6 of 7This code swaps two variables. What does it 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 line correctly unpacks an object into variables that already exist?
Choose an answer to see the explanation.
Key takeaways
- Array patterns take values by position. Object patterns take values by property name.
- A default is used only when the value is
undefined, and it is worked out only when it is needed. - Renaming changes the variable's name. The property keeps its own name.
- In a nested pattern, the path names are not variables. Only the names at the end become variables.
- Add
= {}to a parameter pattern so the function still works with no argument. - Destructuring reads values. It does not copy the objects inside them.
- When a pattern gets long, or the data might be missing, a shorter pattern or plain property access is clearer.
Destructuring is a short way to unpack values: the pattern on the left shows which pieces JavaScript should take from the value on the right.
Before you write a pattern, check three things: the shape of the data, which values might be missing, and whether plain property access would be clearer.
These checks prevent most destructuring mistakes.
Up next: Spread & rest.