JavaScript Destructuring and Spread: Syntax, Rest Patterns, and Worked Examples
Learn how JavaScript destructuring binds values, how rest collects them, and how spread expands them. Follow one object through runnable examples and common traps.
KnowledgeGate Team
Exam prep & CS education

You may understand const name = student.name and still pause when the same job appears as const { name } = student. The confusion grows because ... can collect or expand values. A single source value can be transformed through array and object patterns, defaults, renaming, rest, spread, and shallow-copy traps.
Related reading: JavaScript fundamentals and JavaScript callbacks.
Destructuring means matching a pattern to a value
Destructuring binds array elements or object properties to variables. Direct access and a pattern can match:
const scores = [78, 84, 91];
const first = scores[0];
const [firstAgain] = scores;
console.log(first, firstAgain); // 78 78Pattern shape controls the lookup. Square brackets follow positions, while curly braces use object keys.
const student = { id: 42, name: "Asha", city: "Pune" };
const name = student.name;
const { name: sameName } = student;
const [, secondScore] = scores;
const { city } = student;
console.log(name, sameName); // "Asha" "Asha"
console.log(secondScore, city); // 84 "Pune"An empty slot skips position 0. The shorthand { city } uses the same property and variable name. This foundation helps you build practical web-development skills.
Object destructuring: rename, default, and go nested
Keep this object unchanged for the main extraction:
const student = {
id: 42,
name: "Asha",
city: "Pune",
scores: [78, 84, 91],
contact: { email: "asha@example.com" }
};Rename one property and provide a default:
const { name, city: hometown, rank = 1 } = student;The result is name === "Asha", hometown === "Pune", and rank === 1. In city: hometown, city is the source key and hometown is the variable. The default applies because the missing rank is undefined.
Nested patterns can reach deeper:
const { contact: { email } } = student;
console.log(email); // "asha@example.com"This creates email, not a separate contact variable. A default replaces only undefined, not null:
const { points = 0 } = { points: null };
console.log(points); // nullIt also works in a parameter:
const describe = ({ name, city = "Unknown" }) => `${name} from ${city}`;
console.log(describe(student)); // "Asha from Pune"Array destructuring and rest complete the extraction
Patterns can cross objects and arrays:
const { scores: [first, second, ...remaining] } = student;Here, first is 78, second is 84, and remaining is the new array [91]. One statement handles everything:
const {
id,
name,
city: hometown,
scores: [first, second, ...remaining],
contact: { email },
rank = 1
} = student;
console.log(id, name, hometown, first, second, remaining, email, rank);
// 42 "Asha" "Pune" 78 84 [91] "asha@example.com" 1Inside a binding pattern, rest means "collect what is left". Object rest follows the same rule:
const { id: studentId, contact, ...summary } = student;Now studentId === 42, contact.email === "asha@example.com", and summary exactly equals { name: "Asha", city: "Pune", scores: [78, 84, 91] }. Excluded keys do not appear.

Spread expands values into a new container or argument list
Context decides what ... means. Rest collects in a binding pattern. Spread expands in a literal or function call.
const extendedScores = [...student.scores, 96];
const highest = Math.max(...student.scores);
console.log(extendedScores); // [78, 84, 91, 96]
console.log(highest); // 91With object spread, later properties win:
const updatedStudent = { ...student, city: "Delhi" };
const restoredStudent = { city: "Delhi", ...student };updatedStudent.city is "Delhi", but student.city stays "Pune". In reverse order, the later spread restores the original city, so restoredStudent.city is "Pune".
For an immutable array update, copy both changed containers:
const revisedStudent = {
...student,
scores: [...student.scores, 96]
};revisedStudent.scores is [78, 84, 91, 96], while student.scores remains [78, 84, 91]. You can apply this in a full-stack project when updating state without changing its source.
The shallow-copy trap and four failures to recognise
Spread creates a new outer object, not recursive nested copies:
const clone = { ...student };
clone.contact.email = "asha.new@example.com";
console.log(student.contact.email);
// "asha.new@example.com"Both outer objects initially point to the same contact. Copy that level when it must change independently:
const safeClone = {
...student,
contact: { ...student.contact, email: "safe@example.com" }
};Now safeClone.contact.email is "safe@example.com" without another change to student.contact.email.

Failure or trap | What happens | Fix or rule |
|---|---|---|
| It throws a | Use |
| The pattern is invalid. | Rest must be the last element. |
| A bare block is parsed incorrectly for this assignment. | Wrap it: |
|
| Defaults replace |
How coding tests and interviews use this syntax
Common tasks ask you to predict output, repair a pattern, or transform data without mutation. A broader Coding Round Strategy for Placements helps you explain the reason, not just the output.
Consider overwrite order:
const base = { theme: "light", fontSize: 14 };
const choice = { fontSize: 16, compact: true };
const settings = { ...base, ...choice };The result is { theme: "light", fontSize: 16, compact: true }. choice is spread later, so its fontSize value wins.
Predict the output for each expression and identify whether each use of ... is rest or spread:
What do
headandtailcontain inconst [head, ...tail] = [10, 20, 30, 40]? Is...rest or spread?What does
const sum = (a, b, c) => a + b + c; sum(...[4, 7, 9])return? Is...rest or spread?
For the first, head === 10 and tail is [20, 30, 40]. The pattern collects remaining elements, so ...tail is rest. For the second, 4 + 7 + 9 = 20. The call expands the array into three arguments, so ... is spread.
As a cross-language follow-up, Java String Handling: String Pool and Immutability shows how simple-looking syntax can also hide important value and reference behaviour.
The short version and next step
Use object destructuring when names matter and array destructuring when positions matter. Use rest to collect the remainder, and spread to expand values into a new literal or argument list. Remember the two traps: defaults cover undefined, and spread makes only a shallow copy.
Spend 15 minutes on this exact sequence:
Rewrite three direct property reads from
studentas one object pattern.Extract
78,84, and[91]in one nested pattern.Append
96without changing the original scores array.Make an independent update to the nested
contactobject.
Run every snippet and inspect both the new value and the original. Continue the language sequence with the Complete JavaScript course. Then add a second nested field such as contact.phone = "9876543210", predict exactly which references must be copied, and only then run the code.
Keep learning

Functions and Arrow Functions in JavaScript: Syntax, Scope and Runnable Examples
Build function declarations, expressions and arrows in JavaScript, then trace parameters, closures, lexical this and array callbacks through exact outputs.

Modules in JavaScript: Import, Export, and Runnable Examples
Build JavaScript modules from scratch with a shopping-cart project that runs in Node.js and the browser. Learn live bindings, re-exports, lazy loading, and practical fixes.

LocalStorage and Cookies in JavaScript: Store, Read and Delete Data
Run one lesson-progress object and one preference cookie through their complete life cycles, then learn which browser store fits a feature and which security traps to avoid.

JavaScript Event Delegation: Bubbling, target and currentTarget with Runnable Examples
Learn how browser events travel through the DOM, then build one delegated listener that handles nested controls and rows added later.