JavaScript destructuring, spread, and rest
Unpack objects and arrays into variables, copy with spread, and collect arguments with rest.
const user = { name: "Ada", age: 36, address: { city: "London" } };
const { name, address: { city }, role = "guest" } = user;
console.log(name, city, role);
const updated = { ...user, age: 37 };
console.log(user.age, updated.age, updated.address === user.address);
const [first, , third, ...rest] = [1, 2, 3, 4, 5];
console.log(first, third, rest.join(","));
function total(label, ...numbers) {
return label + ": " + numbers.reduce((sum, n) => sum + n, 0);
}
console.log(total("sum", 1, 2, 3, 4));
const merged = [...[1, 2], ...[3, 4]];
console.log(merged.join(","));Ada London guest 36 37 true 1 3 4,5 sum: 10 1,2,3,4
Explanation
Destructuring pulls values out of arrays and objects into variables in one statement. For objects, the names in braces select properties, a colon renames or goes deeper into a nested object, and = gives a default for a missing value. For arrays, the position decides which element is taken, and a gap skips one.
The spread operator ... copies the elements or properties of one value into a new array or object. {...user, age: 37} makes a shallow copy with a changed age, without modifying the original. The same three dots in a function parameter list, the rest parameter, gather the remaining arguments into an array. A shallow copy shares nested objects with the original.
Explore the dry run
const user = { name: "Ada", age: 36, address: { city: "London" } };const { name, address: { city }, role = "guest" } = user;console.log(name, city, role); const updated = { ...user, age: 37 };console.log(user.age, updated.age, updated.address === user.address); const [first, , third, ...rest] = [1, 2, 3, 4, 5];console.log(first, third, rest.join(",")); function total(label, ...numbers) { return label + ": " + numbers.reduce((sum, n) => sum + n, 0);}console.log(total("sum", 1, 2, 3, 4)); const merged = [...[1, 2], ...[3, 4]];console.log(merged.join(","));Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| Take name, reach into address to get city, and use "guest" because user has no role. | — | — | |
| Three variables from one statement. | — | Ada London guest | |
| A new object with the same properties and a changed age. | — | Ada London guest | |
| The original is unchanged, but the nested address object is shared, because the copy is shallow. | — | Ada London guest 36 37 true | |
| The empty slot skips 2; rest collects what is left. | — | Ada London guest 36 37 true | |
| first is 1, third is 3, and rest is [4, 5]. | — | Ada London guest 36 37 true 1 3 4,5 | |
| The rest parameter numbers is [1, 2, 3, 4]. | — | Ada London guest 36 37 true 1 3 4,5 sum: 10 | |
| Spreading two arrays into one literal concatenates them. | — | Ada London guest 36 37 true 1 3 4,5 sum: 10 1,2,3,4 |
Cost and common mistakes
Copying with spread is O(n) in the number of properties or elements.
- Spread makes a shallow copy. Changing updated.address.city also changes user.address.city. Use structuredClone for a deep copy.
- Destructuring null or undefined throws a TypeError. Provide a default object when the value may be missing.
- Properties on the right of a spread override those on the left, so the order matters.
Try it yourself
How would you swap two variables with destructuring?
Reveal answer and reasoning
[a, b] = [b, a]; the right side builds a new array first, and the left side unpacks it.