Topic 6.3
Destructuring and Spread with Objects
In one line
Destructuring pulls properties out of an object into variables in one line, with renaming, defaults and a rest object for whatever is left; spread ... copies one object's properties into a new one, which is how you merge objects.
Think of it like this
Unpacking a school bag. Instead of saying "take the bag, take out the lunchbox, put it here; take the bag again, take out the water bottle, put it there", you say once: "lunchbox here, bottle there, everything else in the cupboard". Destructuring is that one sentence. Spread is the reverse: tipping several bags into one new bag, where a later bag's lunchbox replaces an earlier one's.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Destructuring
- Pulling values out of an object (or array) into separate variables in one statement.
- Pattern
- The
{ ... }on the left of=that describes which properties to pull out. - Default value
- A fallback in a pattern,
{ role = "guest" }, used when the property isundefined. - Rest property
...restin a pattern: a new object holding all the properties you didn't name.- Spread
...objinside an object literal: copy all ofobj's properties into the new object.- Shallow copy
- A new object whose top-level properties are copied, while nested objects are still shared.
- Enumerable
- A normal, visible property that shows up when you list or copy an object's keys.
- Object.assign
- An older method that copies properties from source objects into a target object.
Step by step
01Pull out two properties
List the keys you want inside braces on the left. Each becomes a variable with the same name.
const user = { name: "Meera", city: "Delhi", age: 15 };
const { name, city } = user;
console.log(name, city); // Meera Delhi02Rename and add defaults
key: newName renames. = value gives a default for missing or undefined values. You can do both: the rename comes first, then the default.
const { city: hometown, role = "student", name: fullName = "Anonymous" } = user;
console.log(hometown, role, fullName); // Delhi student Meera03Destructure a parameter
Functions that take many options usually take one object. Destructuring in the parameter list documents which options exist and gives each one a default. The = {} at the end lets callers pass nothing at all.
function greet({ name = "friend", excited = false } = {}) {
return "Hello, " + name + (excited ? "!" : ".");
}
console.log(greet({ name: "Asha", excited: true })); // Hello, Asha!
console.log(greet()); // Hello, friend.04Leave some keys out with rest
Name the keys you want to drop; ...rest gets everything else as a brand new object. The original is untouched.
const account = { id: 7, email: "a@example.com", password: "secret" };
const { password, ...safe } = account;
console.log(safe); // { id: 7, email: 'a@example.com' }05Merge with spread: later wins
Spread copies properties left to right into a new object. When two sources have the same key, the one further right overwrites the earlier one, but the key keeps its first position.
Try it yourself
- 1
Defaults and null
In the first example, change
role: nulltorole: undefined. Predict the first line before you run it. - 2
Swap the spread order
In the third example, change the merge to
{ ...saved, ...defaults }. Predict thesettingsline: which theme wins now? - 3
Fix the shallow copy
In the last example, change the copy to
{ ...original, members: [...original.members] }. Predict all three lines, then run.
Code & diagrams
`age` was missing, so its default was used. `role` was `null`, which is not `undefined`, so the default `"guest"` was **not** used. Defaults ignore `0` and `""` too.
Preview
Console
Nothing logged yet.Expected console output
Meera Delhi 18 null
0 ""`author: { name }` reaches inside `author`. Its default `{ name: "unknown" }` is used when `author` is missing, and the final `= {}` handles a call with no argument at all.
Preview
Console
Nothing logged yet.Expected console output
Malgudi Days by R. K. Narayan, 256 pages
My Notes by unknown, 0 pages
Untitled by unknown, 0 pagesRest and spread make new objects; `account` and `defaults` are unchanged. `Object.assign` instead writes into its first argument and returns it, skipping `null` sources.
Preview
Console
Nothing logged yet.Expected console output
{ id: 7, email: 'asha@example.com', plan: 'free' }
6 4
{ theme: 'dark', fontSize: 18, sound: false }
{ theme: 'light', fontSize: 16, sound: true }
{ a: 9, b: 2 } trueChanging `copy.name` didn't touch the original, but both objects hold the **same** `members` array. Topic 6.5 shows how to copy nested data too.
Preview
Console
Nothing logged yet.Expected console output
Team A [ 'Asha', 'Ravi', 'Meera' ]
Team B [ 'Asha', 'Ravi', 'Meera' ]
trueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Destructure undefined
Remove = {} from the end of the parameter in the second example and run it.
Break #2
Assign to existing variables without parentheses
Write let a, b; then { a, b } = { a: 1, b: 2 };.
Myth vs fact
Myth
A default in destructuring replaces any empty value.
Fact
It replaces only undefined. null, 0, false and "" are kept. Use ?? after destructuring if null should also fall back.
Myth
{ ...obj } makes a full independent copy.
Fact
It is shallow: nested objects and arrays are shared. Use structuredClone (Topic 6.5) for a deep copy.
Myth
Spread and Object.assign are the same thing.
Fact
Spread always creates a new object and defines properties on it. Object.assign writes into an existing target, so it triggers that target's setters (Topic 6.7) and changes it.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Spread and rest copy only own enumerable properties, including symbol keys. Getters on the source are called and their values copied, so the copy has plain data properties, not getters. Methods inherited from a prototype (including class methods) are not copied, which is why spreading a class instance gives a plain object.
- ▸
Defaults are evaluated lazily, only when needed, and can refer to earlier variables in the same pattern:
const { w = 10, h = w * 2 } = {};givesh= 20. - ▸
Object rest must be the last element and can't have a trailing comma. In parameter lists, a rest property is a common way to forward options:
function Button({ label, ...props }) { ... }, used heavily in React components. - ▸
Merging with spread in a loop (
acc = { ...acc, [k]: v }) copies the whole object every time, which is O(n²) for n keys. In hot loops, mutate a local object you created, or build withObject.fromEntries(Topic 6.4).
Remember this
- 1
Object destructuring (ES2015) creates variables from properties by name:
const { name, age } = user;is the same asconst name = user.name; const age = user.age;. The variable names must match the keys; the order doesn't matter. A key that doesn't exist givesundefined. Destructuringnullorundefinedthrows a TypeError, because there's nothing to read from. - 2
Renaming uses a colon:
const { name: fullName } = user;readsuser.nameinto a variable calledfullName. Read the colon as "goes into". A default value uses=:const { role = "guest" } = user;. The default is used **only when the value isundefined**, not when it isnull,0or"". You can combine both:const { name: fullName = "Anonymous" } = user;. - 3
Nested destructuring follows the shape of the data:
const { address: { city } } = user;creates onlycity(notaddress). Ifaddressis missing it throws, so give it a default:const { address: { city } = {} } = user;. Destructuring also works in function parameters:function show({ title, pages = 0 }) {...}reads the properties from the object passed in. Add= {}after the pattern so calling the function with no argument doesn't throw. - 4
A rest property (ES2018),
const { password, ...rest } = account;, collects all the remaining own enumerable properties (the normal, visible ones) into a new object. It's the clean way to make a copy without some keys, without usingdeleteon the original. - 5
Object spread (ES2018),
{ ...a }, copiesa's own enumerable properties into a new object. With several sources,{ ...defaults, ...saved, sound: false }, properties are copied left to right, so later ones win. That makes spread the standard way to merge settings and to update one field without changing the original:{ ...user, age: 15 }. Spreadingnullorundefinedis allowed and adds nothing. The older equivalent isObject.assign(target, ...sources), which changestargetin place. - 6
Spread makes a shallow copy: the top-level properties are copied, but nested objects and arrays are shared, not duplicated. Change
copy.members.push(...)and the original'smemberschanges too. Topic 6.5 explains why (references) and how to make deep copies. One more syntax rule: to destructure into variables that already exist, wrap the statement in parentheses,({ a, b } = obj);, or the{is read as a block.
Explain it without notes
Explain object destructuring with renaming and defaults, and when a default is used.
What's the difference between object rest and object spread?
Why is { ...obj } called a shallow copy, and what bug does that cause?
How does spread differ from Object.assign?
Practice
Given const movie = { title: "Lagaan", year: 2001, rating: null }, destructure title into name, year, and rating with a default of "not rated". Log all three and explain the rating.
Write withoutKeys(obj) that returns a copy of obj without createdAt and updatedAt, leaving the original unchanged.
Write updateEmail(user, email) that returns a new user with the new email, without changing user. Prove it by logging both.
Trade-offs
- ↔
Destructuring in parameters makes options self-documenting, but deep patterns with renames and defaults become hard to read; past two levels, plain property access is often clearer.
- ↔
Spread makes new objects, which keeps updates safe and easy to track, at the cost of copying; for large objects updated in tight loops, mutating a private local object is faster.
Done when you can
I can destructure properties, rename them and give defaults.
I know a default applies only to
undefined.I can destructure nested objects and function parameters safely with
= {}.I can remove keys with a rest property and merge objects with spread.
I can explain why spread is a shallow copy.