Modern JavaScript has two small pieces of syntax that appear in almost every file: destructuring, which unpacks values from arrays and objects into separate variables, and the three dots ..., which either spread values out or gather them together.
Neither does anything you couldn't do the long way. Their value is in making code shorter and clearer, particularly when you work with objects from servers, function settings and copies of data. Once you recognise them, a lot of modern code becomes much easier to read.
Object destructuring
const user = { name: "Ivy", age: 28, city: "Leeds" };
const { name, city } = user; // pick properties by name
const { age: years } = user; // rename: age becomes years
const { role = "member" } = user; // default if missingThe variable names must match the property names, unless you rename them with a colon. Destructuring is especially common in function parameters, as in function greet({ name }) { … }, which picks out just the property the function needs.
Array destructuring
const rgb = [255, 128, 0];
const [red, green, blue] = rgb; // by position
const [first, , third] = rgb; // skip an item with a comma
let a = 1, b = 2;
[a, b] = [b, a]; // swap two valuesArray destructuring works by position rather than by name, so the order of the variables matters.
Spread: ... expands values
- In arrays,
[...a, ...b]combines two arrays and[...a]makes a copy. - In objects,
{ ...defaults, ...settings }merges them, with later properties winning.{ ...user, age: 29 }copies an object with one change. - In function calls,
Math.max(...numbers)passes the array's items as separate arguments.
Spread makes a shallow copy. The top level is new, but nested arrays and objects are still shared with the original, so changing one of them affects both copies.
Rest: ... gathers values
Rest uses the same three dots, but on the receiving side:
function sum(...nums) { … } // all arguments in one array
const [winner, ...others] = players; // the rest of the array
const { password, ...safeUser } = user; // everything except passwordRest must always come last, because it collects everything that's left over.
Destructuring in function parameters
Destructuring works directly in a function's parameter list, which makes functions that take an options object much easier to read and call:
function createUser({ name, role = "member", active = true } = {}) {
return `${name} is a ${role}${active ? "" : " (inactive)"}`;
}
createUser({ name: "Ana", role: "admin" });The caller passes one object and can list the options in any order, leaving out the ones they don't need. Defaults fill the gaps, and the final = {} means the function still works if it's called with no argument at all. You'll see this pattern in React components, configuration functions and many libraries.
Destructuring and Spread Syntax
Destructuring and spread
const { a, b: renamed, c = 0 } = obj;
const [x, y, ...others] = arr;
const copy = [...arr];
const merged = { ...obj1, ...obj2 };
function f(...args) { }Destructuring and Spread Summary
- Destructuring unpacks values from objects by name and from arrays by position.
- You can rename properties and set default values while destructuring.
- Spread (
...) copies and merges arrays and objects, and passes array items as arguments. - Spread copies are shallow; nested values are still shared.
- Rest (
...) gathers remaining values and must come last.
Destructuring and Spread Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Destructuring objects
const movie = {
title: "Spirited Away",
year: 2001,
director: { name: "Hayao Miyazaki" },
};
const { title, year } = movie;
const { rating = "Not rated" } = movie;
const { director: { name: directorName } } = movie;
console.log(title, year);
console.log(rating);
console.log(directorName);Line-by-line explanation
- Line 7Two variables from two properties in one line.
- Line 8A default value for a property that doesn't exist.
- Line 9Reach into a nested object and rename the result.
Destructuring in function parameters
Very common in real code: destructure the object right in the parameter list.
function describe({ name, species, legs = 4 }) {
return `${name} the ${species} has ${legs} legs`;
}
console.log(describe({ name: "Pip", species: "parrot", legs: 2 }));
console.log(describe({ name: "Rex", species: "dog" }));
const [winner, runnerUp, ...rest] = ["Ola", "Ben", "Cy", "Di"];
console.log(winner, runnerUp, rest);Line-by-line explanation
- Line 1The function receives an object and unpacks it immediately, with a default for
legs. - Line 6Rex gets the default 4 legs.
- Line 8Array destructuring with rest gathers the remaining items.
Spread for copies and merges
const defaults = { theme: "light", fontSize: 16, sound: true };
const userPrefs = { theme: "dark" };
const settings = { ...defaults, ...userPrefs };
console.log(settings);
const updated = { ...settings, fontSize: 18 };
console.log(settings.fontSize, updated.fontSize);
const week1 = [3, 5];
const week2 = [8, 1];
const all = [...week1, ...week2, 10];
console.log(all, Math.max(...all));
const { sound, ...visual } = settings;
console.log(visual);Line-by-line explanation
- Line 4Merge objects.
userPrefscomes last, so itsthemewins. - Line 7A copy with one change. The original is untouched.
- Line 12Combine arrays and add an extra value.
- Line 13Spread an array into separate arguments.
- Line 15Rest in destructuring: everything except
sound.
Common Destructuring and Spread Mistakes
Spread copies are shallow
const a = { tags: ["x"] };
const b = { ...a };
b.tags.push("y");
console.log(a.tags); // ["x", "y"]: the nested array is shared!const b = { ...a, tags: [...a.tags] };
// or: const b = structuredClone(a);Spread copies the top level only. Nested objects/arrays are still shared.
Destructuring from undefined
const { name } = undefined; // TypeErrorconst { name } = user ?? {};Destructuring and Spread in Real Projects
Destructuring in modern frameworks
React components receive props like function Card({ title, image }). State updates use spread: setUser({ ...user, name: "New" }). APIs return objects you destructure: const { data, error } = await fetchThing(). You'll see these patterns constantly.
Destructuring and Spread Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Pull out name and price, and combine the two arrays.
const product = { name: "Mug", price: 8 };
const { , } = product;
const all = [ a, b ];
....What does this print?
const [a, , c = 9, d = 4] = [1, 2, 3];
console.log(a, c, d);What does this print?
const base = { size: "M", color: "red" };
const order = { ...base, color: "blue", qty: 2 };
console.log(order.color, base.color);color wins in the copy; the original is unchanged.average(…) should accept any number of arguments and return their average. average(2, 4, 6) should be 4. Fix the function.
function average(nums) {
const total = nums.reduce((s, n) => s + n, 0);
return total / nums.length;
}
console.log(average(2, 4, 6));- average(2, 4, 6) is 4
- average(10) is 10
Destructuring and Spread Quiz
3 questions to check what you learned.
What does
const [x, y] = [7, 8];do?- Sets x to 7 and y to 8
- Creates an array
- Sets x and y to [7, 8]
- Throws an error
What does
{ ...a, ...b }do when both have a keyid?- Throws an error
- Keeps b's id (the later one wins)
- Keeps a's id
- Creates an array of both ids
In
function f(...args), what isargs?- The first argument
- A string
- An object
- An array of all the arguments
Destructuring and Spread Challenge
Safe user profile
Given user:
- Use object destructuring with rest to create
publicProfile, containing everything exceptpassword - Create
updatedas a copy ofpublicProfilewithcitychanged to"Glasgow"(using spread) - Destructure
usernamefromupdatedand logskye_r lives in Glasgow
const user = { username: "skye_r", password: "hunter2", city: "Dundee", age: 24 };
- publicProfile has no password
- updated has city Glasgow
- publicProfile was not changed
- Logs the sentence