Objects, Destructuring and JSON · Lesson 19 of 40
Destructuring and the Spread Operator
Destructuring pulls values out of objects and arrays into variables in one line. Spread (...) does the opposite: it expands an object or array into a new one. Together they appear in nearly every modern codebase – React props, API responses, Redux reducers, function options – so reading them fluently is a must.
In this lesson
- Object destructuring: renaming, defaults, nesting
- Rest properties to collect the remainder
- Destructuring function parameters
- Spread to copy, merge and update immutably
Object destructuring
const response = {
id: 501,
title: "JavaScript Basics",
author: { name: "Asha", followers: 1200 },
price: 0,
};
const { id, title } = response;
console.log(id, title);
const { title: courseName, price: cost } = response; // rename
console.log(courseName, cost);
const { rating = 4.5, price = 999 } = response; // defaults
console.log(rating, price); // price exists (0), so no default
const { author: { name: authorName } } = response; // nested
console.log(authorName);
501 JavaScript Basics JavaScript Basics 0 4.5 0 Asha
Reading the destructuring patterns
{ id }id = obj.idsame name{ title: courseName }courseName = obj.titlekey: newName{ rating = 4.5 }obj.rating ?? 4.5**only for undefined{ author: { name } }name = obj.author.namenested
Common mistake: destructuring undefined
const { name } = undefined; throws a TypeError. When the object might be missing, give it a fallback: const { name } = data ?? {};. Nested destructuring throws too if a middle level is missing.
Rest properties
const user = { id: 7, name: "Ravi", password: "hash$2b...", email: "ravi@example.com" };
const { password, ...publicUser } = user; // remove a field without mutating
console.log(publicUser);
{ id: 7, name: 'Ravi', email: 'ravi@example.com' }This is the cleanest way to strip sensitive fields before sending an object to the browser or a log.
Destructuring parameters
function createInvoice({ customer, items, taxRate = 0.18, currency = "INR" } = {}) {
const subtotal = items.reduce((sum, { price, qty = 1 }) => sum + price * qty, 0);
const total = subtotal * (1 + taxRate);
return `${customer}: ${currency} ${total.toFixed(2)}`;
}
console.log(createInvoice({
customer: "Meera",
items: [{ price: 500, qty: 2 }, { price: 250 }],
}));
Meera: INR 1475.00
The = {} default means calling createInvoice() with no argument fails with a clear error about items rather than "cannot destructure undefined". Destructured parameters also document which options a function accepts.
Spread for objects
const defaults = { theme: "light", fontSize: 16, notifications: true };
const saved = { theme: "dark" };
const settings = { ...defaults, ...saved }; // later keys win
console.log(settings);
const updated = { ...settings, fontSize: 18 }; // copy with one change
console.log(settings.fontSize, updated.fontSize);
{ theme: 'dark', fontSize: 16, notifications: true }
16 18Updating nested state immutably
const state = {
user: { name: "Asha", prefs: { lang: "en", theme: "light" } },
cart: [{ id: 1, qty: 1 }],
};
const next = {
...state,
user: { ...state.user, prefs: { ...state.user.prefs, theme: "dark" } },
cart: state.cart.map((item) => (item.id === 1 ? { ...item, qty: item.qty + 1 } : item)),
};
console.log(state.user.prefs.theme, next.user.prefs.theme);
console.log(state.cart[0].qty, next.cart[0].qty);
console.log(state.user === next.user, state.cart === next.cart);
light dark 1 2 false false
Immutable update: copy each level you change
- Copy the outer object{ ...state }
- Copy the path down to the changeuser → prefs
- Set the new valuetheme: "dark"
- Untouched branches are sharedcheap, and change detection still works
Spread for arrays and calls
const a = [1, 2], b = [3, 4];
console.log([...a, ...b], [0, ...a, 99]);
console.log(Math.max(...b));
console.log([..."hello"].length, [...new Set("banana")].join(""));
[ 1, 2, 3, 4 ] [ 0, 1, 2, 99 ] 4 5 ban
Same ..., two jobs | Where | Example |
|---|---|---|
| Spread – expands | in literals and calls | [...arr], { ...obj }, fn(...args) |
| Rest – collects | in parameters and destructuring | (...args) =>, const { a, ...rest } = obj |
Practice
Try it yourself
From
{ data: { user: { id: 9, profile: { city: "Goa" } } } }, destructureidandcityin one statement.Show solution
const res = { data: { user: { id: 9, profile: { city: "Goa" } } } }; const { data: { user: { id, profile: { city } } } } = res; console.log(id, city);9 Goa
Write
omit(obj, key)that returns a copy without that key.Show solution
const omit = (obj, key) => { const { [key]: _removed, ...rest } = obj; return rest; }; console.log(omit({ a: 1, b: 2, c: 3 }, "b"));{ a: 1, c: 3 }
Quick check
In { ...a, ...b }, whose value wins for a shared key?
b's – later spreads overwrite earlier ones.
Does a destructuring default apply when the value is null?
No, only when it is undefined.
Is object spread a deep copy?
No – nested objects are shared; copy each level you change.
Summary
- Destructuring extracts values with renaming, defaults and nesting.
- Rest collects remaining properties – great for removing fields.
- Spread copies and merges; update nested state by copying each level.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.