Introduction
Open almost any modern JavaScript file and you will see { name }, ...items and user?.email. These short forms were added to the language because developers kept writing the same long code by hand: pulling out properties one at a time, copying lists with loops, and checking for missing data at every step. Learning them makes other people's code readable.
How it works
Destructuring unpacks values into variables. In an array, position matters. In an object, the name matters.
const [first, second] = [10, 20];
const { name, age } = student;
const { city: town } = student; // a different variable name
const { phone = "none" } = student; // a default if it is missing
Spread expands a list or an object where it is written. It is how you copy without sharing, and how you make a new object with one thing changed. When two objects have the same key, the later one wins, which is why defaults go first: { ...defaults, ...choices }.
Spread is shallow. Only the outer level is copied, so an array inside the object is still shared.
Rest uses the same three dots to do the opposite: it gathers things up. In const [head, ...tail] = list the variable tail gets all the remaining items, and in function total(...prices) the parameter prices is an array of all the arguments.
Optional chaining. user.address.city throws a TypeError if address is missing. user.address?.city gives undefined instead. Each ?. protects only the step just after it.
The nullish operator. a ?? b gives b only when a is null or undefined. The older form, a || b, gives b for every falsy value, so a real 0 or empty string is lost. That difference is shown in this example:
// Reading data that might be missing.
const orders = [
{ id: 1, customer: { name: "Arif", phone: null }, discount: 0 },
{ id: 2, customer: null },
];
for (const order of orders) {
const name = order.customer?.name ?? "Guest";
const phone = order.customer?.phone ?? "no phone";
const wrong = order.discount || 5; // 0 is falsy, so a real 0 is lost
const right = order.discount ?? 5; // only null or undefined get the default
console.log(order.id, name, phone, wrong, right);
}
function total(...prices) {
return prices.reduce((sum, p) => sum + p, 0);
}
console.log(total(10, 20), total());Syntax
const copy = [...list];
const updated = { ...user, age: 22 };
const label = user.nickname ?? user.name;
const city = user.address?.city ?? "unknown";
function sum(...numbers) { return numbers.reduce((a, b) => a + b, 0); }
Example
// Unpacking arrays and objects, and copying with spread.
const point = [3, 4];
const [x, y] = point;
console.log(x, y);
const user = { name: "Mitu", city: "Dhaka", age: 21 };
const { name, city: town, phone = "none" } = user;
console.log(name, town, phone);
const [first, ...others] = [10, 20, 30];
console.log(first, others);
const older = { ...user, age: 22 };
console.log(user.age, older.age);
const both = [...point, ...others];
console.log(both);Line by line
const [x, y] = pointtakes the items by position.namematches its property.city: townreadscityinto a variable calledtown.phoneis missing, so it gets the default.[first, ...others]puts the first item infirst, and the rest into a new array.{ ...user, age: 22 }copiesuserand then replacesagein the copy. The original is unchanged.[...point, ...others]builds one list from two.
Expected result
3 4
Mitu Dhaka none
10 [ 20, 30 ]
21 22
[ 3, 4, 20, 30 ]
In the real world
A server's answer rarely has every field filled in, so ?. and ?? are everywhere in code that shows that data. React code uses { ...state, loading: false } constantly, because it compares old and new objects, and so needs a new object rather than a changed one.
Common mistakes
- Using
||for a default when 0 or empty text is valid. Use??. - Believing spread makes a deep copy. Nested objects and arrays are still shared.
- Destructuring something that is null.
const { a } = nullis a TypeError. - Using
?.everywhere. If a value must exist, an error tells you quickly that something is wrong, while?.hides it.
Best practices
- Put defaults first and overrides last when you merge objects.
- Use
?.only where a value can really be missing. - Destructure only the few properties you need.
Summary
Destructuring unpacks arrays by position and objects by name. Spread expands, and rest gathers. ?. stops safely at a missing value, and ?? gives a default only for null and undefined.
Quick check
Key takeaways
- Spread makes a shallow copy, so objects inside are still shared.
- ?. protects one step of a chain.
- ?? uses the default only for null and undefined, while || also replaces 0 and empty text.
Practice problems for this lesson
My notes
Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.